Example #1
0
  def _VerifyHMAC(self, comms=None):
    """Verifies the HMAC.

    This method raises a DecryptionError if the received HMAC does not
    verify. If the HMAC verifies correctly, True is returned.

    Args:
      comms: The comms RdfValue to verify.

    Raises:
      DecryptionError: The HMAC did not verify.

    Returns:
      True

    """
    # Check the encrypted message integrity using HMAC.
    if self.hmac_type == "SIMPLE_HMAC":
      digest = self.HMAC(comms.encrypted)
      if not hmac.compare_digest(comms.hmac, digest):
        raise DecryptionError("HMAC verification failed.")

    elif self.hmac_type == "FULL_HMAC":
      digest = self.HMAC(comms.encrypted, comms.encrypted_cipher,
                         comms.encrypted_cipher_metadata,
                         comms.packet_iv.SerializeToString(),
                         struct.pack("<I", comms.api_version))

      if not hmac.compare_digest(comms.full_hmac, digest):
        raise DecryptionError("HMAC verification failed.")

    else:
      raise DecryptionError("HMAC type no supported.")

    return True
Example #2
0
    async def auth_middleware(request, handler):
        """Authenticate as middleware."""
        # If no password set, just always set authenticated=True
        if api_password is None:
            request[KEY_AUTHENTICATED] = True
            return await handler(request)

        # Check authentication
        authenticated = False

        if (HTTP_HEADER_HA_AUTH in request.headers and
                hmac.compare_digest(
                    api_password, request.headers[HTTP_HEADER_HA_AUTH])):
            # A valid auth header has been set
            authenticated = True

        elif (DATA_API_PASSWORD in request.query and
              hmac.compare_digest(api_password,
                                  request.query[DATA_API_PASSWORD])):
            authenticated = True

        elif (hdrs.AUTHORIZATION in request.headers and
              validate_authorization_header(api_password, request)):
            authenticated = True

        elif _is_trusted_ip(request, trusted_networks):
            authenticated = True

        request[KEY_AUTHENTICATED] = authenticated
        return await handler(request)
Example #3
0
def serve_metrics(request):
    if not settings.METRICS_ENABLED:
        return unauthed_response()

    # check if the user is properly authorized:
    if "HTTP_AUTHORIZATION" not in request.META:
        return unauthed_response()

    method, credentials = request.META["HTTP_AUTHORIZATION"].split(" ", 1)
    if method.lower() != "basic":
        return unauthed_response()

    user, passphrase = base64.b64decode(credentials.strip()).decode().split(":", 1)

    if not hmac.compare_digest(user, settings.METRICS_USER):
        return unauthed_response()
    if not hmac.compare_digest(passphrase, settings.METRICS_PASSPHRASE):
        return unauthed_response()

    # ok, the request passed the authentication-barrier, let's hand out the metrics:
    m = metrics.metric_values()

    output = []
    for metric, sub in m.items():
        for label, value in sub.items():
            output.append("{}{} {}".format(metric, label, str(value)))

    content = "\n".join(output) + "\n"

    return HttpResponse(content)
Example #4
0
async def async_secure_get_client(hass, client_id, client_secret):
    """Get a client id/secret in consistent time."""
    client = await hass.auth.async_get_client(client_id)

    if client is None:
        if client_secret is not None:
            # Still do a compare so we run same time as if a client was found.
            hmac.compare_digest(client_secret.encode('utf-8'),
                                client_secret.encode('utf-8'))
        return None

    if client.secret is None:
        return client

    elif client_secret is None:
        # Still do a compare so we run same time as if a secret was passed.
        hmac.compare_digest(client.secret.encode('utf-8'),
                            client.secret.encode('utf-8'))
        return None

    elif hmac.compare_digest(client_secret.encode('utf-8'),
                             client.secret.encode('utf-8')):
        return client

    return None
Example #5
0
    async def auth_middleware(request, handler):
        """Authenticate as middleware."""
        authenticated = False

        if use_auth and (HTTP_HEADER_HA_AUTH in request.headers or
                         DATA_API_PASSWORD in request.query):
            if request.path not in old_auth_warning:
                _LOGGER.log(
                    logging.INFO if support_legacy else logging.WARNING,
                    'You need to use a bearer token to access %s from %s',
                    request.path, request[KEY_REAL_IP])
                old_auth_warning.add(request.path)

        if (hdrs.AUTHORIZATION in request.headers and
                await async_validate_auth_header(
                    request, api_password if legacy_auth else None)):
            # it included both use_auth and api_password Basic auth
            authenticated = True

        # We first start with a string check to avoid parsing query params
        # for every request.
        elif (request.method == "GET" and SIGN_QUERY_PARAM in request.query and
              await async_validate_signed_request(request)):
            authenticated = True

        elif (legacy_auth and HTTP_HEADER_HA_AUTH in request.headers and
              hmac.compare_digest(
                  api_password.encode('utf-8'),
                  request.headers[HTTP_HEADER_HA_AUTH].encode('utf-8'))):
            # A valid auth header has been set
            authenticated = True
            request['hass_user'] = await legacy_api_password.async_get_user(
                app['hass'])

        elif (legacy_auth and DATA_API_PASSWORD in request.query and
              hmac.compare_digest(
                  api_password.encode('utf-8'),
                  request.query[DATA_API_PASSWORD].encode('utf-8'))):
            authenticated = True
            request['hass_user'] = await legacy_api_password.async_get_user(
                app['hass'])

        elif _is_trusted_ip(request, trusted_networks):
            users = await app['hass'].auth.async_get_users()
            for user in users:
                if user.is_owner:
                    request['hass_user'] = user
                    break
            authenticated = True

        elif not use_auth and api_password is None:
            # If neither password nor auth_providers set,
            #  just always set authenticated=True
            authenticated = True

        request[KEY_AUTHENTICATED] = authenticated
        return await handler(request)
Example #6
0
    def handle_request(self, request, **values):
        """Handle request to url."""
        from werkzeug.exceptions import MethodNotAllowed, Unauthorized

        if request.method == "OPTIONS":
            # For CORS preflight requests.
            return self.options(request)

        try:
            handler = getattr(self, request.method.lower())
        except AttributeError:
            raise MethodNotAllowed

        # Auth code verbose on purpose
        authenticated = False

        if self.hass.wsgi.api_password is None:
            authenticated = True

        elif request.remote_addr in self.hass.wsgi.approved_ips:
            authenticated = True

        elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''),
                                 self.hass.wsgi.api_password):
            # A valid auth header has been set
            authenticated = True

        elif hmac.compare_digest(request.args.get(DATA_API_PASSWORD, ''),
                                 self.hass.wsgi.api_password):
            authenticated = True

        if self.requires_auth and not authenticated:
            _LOGGER.warning('Login attempt or request with an invalid '
                            'password from %s', request.remote_addr)
            persistent_notification.create(
                self.hass,
                'Invalid password used from {}'.format(request.remote_addr),
                'Login attempt failed', NOTIFICATION_ID_LOGIN)
            raise Unauthorized()

        request.authenticated = authenticated

        _LOGGER.info('Serving %s to %s (auth: %s)',
                     request.path, request.remote_addr, authenticated)

        result = handler(request, **values)

        if isinstance(result, self.Response):
            # The method handler returned a ready-made Response, how nice of it
            return result

        status_code = 200

        if isinstance(result, tuple):
            result, status_code = result

        return self.Response(result, status=status_code)
Example #7
0
def verify_token(token, secret, i=None, window_size=256):
    if i is None:
        return hmac.compare_digest(token, get_token(secret))
    n = 0
    for i in range(i, i + window_size):
        n += 1
        if hmac.compare_digest(token, get_token(secret, i)):
            return n
    return False
Example #8
0
def strings_equal(s1, s2):
    """
    Timing-attack resistant string comparison.

    Normal comparison using == will short-circuit on the first mismatching
    character. This avoids that by scanning the whole string, though we
    still reveal to a timing attack whether the strings are the same
    length.
    """
    try:
        # Python 3.3+ and 2.7.7+ include a timing-attack-resistant
        # comparison function, which is probably more reliable than ours.
        # Use it if available.
        from hmac import compare_digest

        return compare_digest(s1, s2)
    except ImportError:
        pass

    if len(s1) != len(s2):
        return False

    differences = 0
    for c1, c2 in zip(s1, s2):
        differences |= ord(c1) ^ ord(c2)
    return differences == 0
Example #9
0
def check_session_id_signature(session_id, secret_key=settings.secret_key_bytes(),
                               signed=settings.sign_sessions()):
    """Check the signature of a session ID, returning True if it's valid.

    The server uses this function to check whether a session ID
    was generated with the correct secret key. If signed sessions are disabled,
    this function always returns True.

    Args:
        session_id (str) : The session ID to check
        secret_key (str, optional) : Secret key (default: value of 'BOKEH_SECRET_KEY' env var)
        signed (bool, optional) : Whether to check anything (default: value of
                                  'BOKEH_SIGN_SESSIONS' env var)

    """
    secret_key = _ensure_bytes(secret_key)
    if signed:
        pieces = session_id.split('-', 1)
        if len(pieces) != 2:
            return False
        base_id = pieces[0]
        provided_signature = pieces[1]
        expected_signature = _signature(base_id, secret_key)
        # hmac.compare_digest() uses a string compare algorithm that doesn't
        # short-circuit so we don't allow timing analysis
        # encode_utf8 is used to ensure that strings have same encoding
        return hmac.compare_digest(encode_utf8(expected_signature), encode_utf8(provided_signature))
    else:
        return True
    def _postreceive(self):
        """Callback from Flask"""

        digest = self._get_digest()

        if digest is not None:
            sig_parts = _get_header('X-Hub-Signature').split('=', 1)

            if (len(sig_parts) < 2 or sig_parts[0] != 'sha1'
                    or not hmac.compare_digest(sig_parts[1], unicode(digest))):
                abort(400, 'Invalid signature')

        event_type = _get_header('X-Github-Event')
        data = request.get_json()

        if data is None:
            abort(400, 'Request body must contain json')

        self._logger.info(
            '%s (%s)', _format_event(event_type, data), _get_header('X-Github-Delivery'))

        for hook in self._hooks.get(event_type, []):
            hook(data)

        return '', 204
Example #11
0
def strings_differ(string1, string2):
    """Check whether two strings differ while avoiding timing attacks.

    This function returns True if the given strings differ and False
    if they are equal.  It's careful not to leak information about *where*
    they differ as a result of its running time, which can be very important
    to avoid certain timing-related crypto attacks:

        http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf

    """
    len_eq = len(string1) == len(string2)
    if len_eq:
        invalid_bits = 0
        left = string1
    else:
        invalid_bits = 1
        left = string2
    right = string2

    if compare_digest is not None:
        invalid_bits += not compare_digest(left, right)
    else:
        for a, b in zip(left, right):
            invalid_bits += a != b
    return invalid_bits != 0
Example #12
0
 def validate_token(self, user, token_hex):
     self._purge_old_tokens()
     possible_tokens = user.tokens
     for token in possible_tokens:
         if hmac.compare_digest(bytes(token_hex), bytes(token.value)):
             return True
     return False
Example #13
0
    def deserialize(self, msg_list, content=True, copy=True):
        """Unserialize a msg_list to a nested message dict.

        This is roughly the inverse of serialize. The serialize/deserialize
        methods work with full message lists, whereas pack/unpack work with
        the individual message parts in the message list.

        Parameters
        ----------
        msg_list : list of bytes or Message objects
            The list of message parts of the form [HMAC,p_header,p_parent,
            p_metadata,p_content,buffer1,buffer2,...].
        content : bool (True)
            Whether to unpack the content dict (True), or leave it packed
            (False).
        copy : bool (True)
            Whether msg_list contains bytes (True) or the non-copying Message
            objects in each place (False).

        Returns
        -------
        msg : dict
            The nested message dict with top-level keys [header, parent_header,
            content, buffers].  The buffers are returned as memoryviews.
        """
        minlen = 5
        message = {}
        if not copy:
            # pyzmq didn't copy the first parts of the message, so we'll do it
            for i in range(minlen):
                msg_list[i] = msg_list[i].bytes
        if self.auth is not None:
            signature = msg_list[0]
            if not signature:
                raise ValueError("Unsigned Message")
            if signature in self.digest_history:
                raise ValueError("Duplicate Signature: %r" % signature)
            self._add_digest(signature)
            check = self.sign(msg_list[1:5])
            if not compare_digest(signature, check):
                raise ValueError("Invalid Signature: %r" % signature)
        if not len(msg_list) >= minlen:
            raise TypeError("malformed message, must have at least %i elements"%minlen)
        header = self.unpack(msg_list[1])
        message['header'] = extract_dates(header)
        message['msg_id'] = header['msg_id']
        message['msg_type'] = header['msg_type']
        message['parent_header'] = extract_dates(self.unpack(msg_list[2]))
        message['metadata'] = self.unpack(msg_list[3])
        if content:
            message['content'] = self.unpack(msg_list[4])
        else:
            message['content'] = msg_list[4]
        buffers = [memoryview(b) for b in msg_list[5:]]
        if buffers and buffers[0].shape is None:
            # force copy to workaround pyzmq #646
            buffers = [memoryview(b.bytes) for b in msg_list[5:]]
        message['buffers'] = buffers
        # adapt to the current version
        return adapt(message)
Example #14
0
def foo():
    print "testing the webhook"

    sha_name, signature = request.headers.get('X-Hub-Signature').split('=')

    if sha_name != 'sha1':
        print "sha name doesnt equal sha1"

    mac = hmac.new(str('secret'), msg=request.data, digestmod=sha1)

    if (hmac.compare_digest(str(mac.hexdigest()), str(signature)) and
            request.headers.get('X-GitHub-Event') == u'push'):


        api_data = json.loads(request.data)
        print api_data
        print 'SECRET WAS A SUCCESS!'

        success_message = {
            'status': 200,
            'message': "Github Webhook: success!"
        }

        response = jsonify(success_message)
        response.status_code = 200
    else:
        failure_message = {
            'status': 400,
            'message': "Github Webhook: failed!"
        }

        response = jsonify(failure_message)
        response.status_code = 400

    return response
Example #15
0
    def __call__(self, environ, start_response):
        # Determine if the request comes from a trusted proxy or not by looking
        # for a token in the request.
        request_token = environ.get("HTTP_WAREHOUSE_TOKEN")
        if (request_token is not None
                and hmac.compare_digest(self.token, request_token)):
            # Compute our values from the environment.
            proto = environ.get("HTTP_WAREHOUSE_PROTO", "")
            remote_addr = environ.get("HTTP_WAREHOUSE_IP", "")
            host = environ.get("HTTP_WAREHOUSE_HOST", "")

            # Put the new header values into our environment.
            if remote_addr:
                environ["REMOTE_ADDR"] = remote_addr
            if host:
                environ["HTTP_HOST"] = host
            if proto:
                environ["wsgi.url_scheme"] = proto

        # Remove any of the forwarded or warehouse headers from the environment
        for header in {
                "HTTP_X_FORWARDED_PROTO", "HTTP_X_FORWARDED_FOR",
                "HTTP_X_FORWARDED_HOST", "HTTP_WAREHOUSE_TOKEN",
                "HTTP_WAREHOUSE_PROTO", "HTTP_WAREHOUSE_IP",
                "HTTP_WAREHOUSE_HOST"}:
            if header in environ:
                del environ[header]

        # Dispatch to the real underlying application.
        return self.app(environ, start_response)
Example #16
0
 def post(self, request, *args, **kwargs):
     secret = self.get_secret()
     if not secret:
         raise ImproperlyConfigured('GitHub webhook secret ist not defined.')
     if 'HTTP_X_HUB_SIGNATURE' not in request.META:
         return HttpResponseBadRequest('Request does not contain X-GITHUB-SIGNATURE header')
     if 'HTTP_X_GITHUB_EVENT' not in request.META:
         return HttpResponseBadRequest('Request does not contain X-GITHUB-EVENT header')
     digest_name, signature = request.META['HTTP_X_HUB_SIGNATURE'].split('=')
     if digest_name != 'sha1':
         return HttpResponseBadRequest('Unsupported X-HUB-SIGNATURE digest mode found: {}'.format(digest_name))
     mac = hmac.new(
         secret.encode('utf-8'),
         msg=request.body,
         digestmod=hashlib.sha1
     )
     if not hmac.compare_digest(mac.hexdigest(), signature):
         return HttpResponseBadRequest('Invalid X-HUB-SIGNATURE header found')
     event = request.META['HTTP_X_GITHUB_EVENT']
     if event not in self.get_allowed_events():
         return HttpResponseBadRequest('Unsupported X-GITHUB-EVENT header found: {}'.format(event))
     handler = getattr(self, event, None)
     if not handler:
         return HttpResponseBadRequest('Unsupported X-GITHUB-EVENT header found: {}'.format(event))
     payload = json.loads(request.body.decode('utf-8'))
     response = handler(payload, request, *args, **kwargs)
     return JsonResponse(response)
Example #17
0
	def get_secure_cookie(self, key, max_time):
		'''
		decode and verify a cookie set with :func:`Response.set_secure_cookie`

		:param key: ``key`` passed to ``set_secure_cookie``
		:type max_time: `datetime.timedelta <https://docs.python.org/3/library/datetime.html#timedelta-objects>`_
		  or None
		:param max_time: amount of time since cookie was set that it should be considered valid for.
		  this is normally equal to the ``max_age`` passed to ``set_secure_cookie``. longer times mean
		  larger windows during which a replay attack is valid. this can be None, in which case no
		  expiry check is performed
		:rtype: str or None
		'''
		try:
			cookie = self.cookies[key].value
		except KeyError:
			return None
		try:
			value, ts, signature = cookie.rsplit('|', 2)
			ts = int(ts)
		except ValueError:
			raise exceptions.HTTPException(400, 'invalid %s cookie: %s' % (key, cookie))
		value_ts = '%s|%d' % (value, int(ts))
		if hmac.compare_digest(signature, _hash(key + '|' + value_ts, self.app.cookie_secret)):
			if max_time is not None and ts + max_time.total_seconds() < time.time(): # cookie has expired
				return None
			return value
		else:
			return None
Example #18
0
def add_issue_bot(request):
    request_body = request.body.decode('utf-8')
    data = json.loads(request_body)
    mac = request.META.get('HTTP_X_MAC', None)
    computed_mac = hmac.new(settings.SHORA_SIGN_SECRET.encode('utf-8'))
    computed_mac.update(request_body.encode('utf-8'))
    if not mac or not hmac.compare_digest(mac, str(computed_mac.hexdigest())):
        logger.warning('HMAC authorization failed for bot. Header: ' + mac)
        return JsonResponse({
            'success': False,
            'message': 'Not authorized'
        })

    form = BotIssueForm(data=data)
    if form.is_valid():
        Issue.objects.create(
            sender=None,
            title=form.cleaned_data['title'],
            category=30,
            category_optional=form.cleaned_data['place'],
            message=form.cleaned_data['description'],
            status=1,
        )
        return JsonResponse({
            'success': True,
            'message': 'Issue created'
        })
    else:
        logger.error('Bot bad request')
        return JsonResponse({
            'success': False,
            'message': 'Data format error in following fields: %s' % list(', '.join(form.errors))
        })
Example #19
0
 def verify(self, password, encoded):
     algorithm, iterations, salt, hash = encoded.split('$', 3)
     if not algorithm == self.algorithm:
         return False
     encoded_2 = self.encode(password, salt, int(iterations))
     return hmac.compare_digest(force_bytes(encoded),
                                force_bytes(encoded_2))
Example #20
0
File: 9.py Project: bodii/test-code
def server_authenticate(connection, secret_key):
    message = os.urandom(32)
    connection.send(message)
    hash = hmac.new(secret_key, message)
    digest = hash.digest()
    response = connection.recv(len(digest))
    return hmac.compare_digest(digest, response)
Example #21
0
def verify_hmac_hash(data, signature):
    #GITHUB_TOKEN = settings.GITHUB_WEBHOOK_TOKEN
    path = "/.simpleHTTPServer_env"
    load_dotenv(path)
    GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
    mac = hmac.new(GITHUB_TOKEN, msg=data, digestmod=hashlib.sha1)
    return hmac.compare_digest('sha1=' + mac.hexdigest(), signature)
Example #22
0
def post(*arg, **kwarg):
    '''
    This is the main listener for github webhooks.
    '''

    # retrieve the headers from the request
    # headers = tangelo.request_headers() # <- not merged
    headers = cherrypy.request.headers

    # get the request body as a dict
    body = tangelo.request_body()
    s = body.read()

    # make sure this is a valid request coming from github
    computed_hash = hmac.new(str(_secret_key), s, hashlib.sha1).hexdigest()
    received_hash = headers.get('X-Hub-Signature', 'sha1=')[5:]
    if not hmac.compare_digest(computed_hash, received_hash):
        return tangelo.HTTPStatusCode(403, "Invalid signature")

    try:
        obj = json.loads(s)
    except:
        return tangelo.HTTPStatusCode(400, "Could not load json object.")

    if headers['X-Github-Event'] == 'push':
        # add a new item to the test queue
        add_push(obj)
    else:
        return tangelo.HTTPStatusCode(400, "Unhandled event")

    return 'OK'
Example #23
0
    def is_authenticated(self, user, password):
        """Validate credentials.

        Iterate through htpasswd credential file until user matches, extract
        hash (encrypted password) and check hash against user-given password,
        using the method specified in the Radicale config.

        The content of the file is not cached because reading is generally a
        very cheap operation, and it's useful to get live updates of the
        htpasswd file.

        """
        try:
            with open(self.filename) as f:
                for line in f:
                    line = line.rstrip("\n")
                    if line.lstrip() and not line.lstrip().startswith("#"):
                        try:
                            login, hash_value = line.split(":", maxsplit=1)
                            # Always compare both login and password to avoid
                            # timing attacks, see #591.
                            login_ok = hmac.compare_digest(login, user)
                            password_ok = self.verify(hash_value, password)
                            if login_ok and password_ok:
                                return True
                        except ValueError as e:
                            raise RuntimeError("Invalid htpasswd file %r: %s" %
                                               (self.filename, e)) from e
        except OSError as e:
            raise RuntimeError("Failed to load htpasswd file %r: %s" %
                               (self.filename, e)) from e
        return False
Example #24
0
File: key.py Project: c0ns0le/borg
 def decrypt(self, id, data):
     if not (data[0] == self.TYPE or
         data[0] == PassphraseKey.TYPE and isinstance(self, RepoKey)):
         raise IntegrityError('Invalid encryption envelope')
     hmac_given = memoryview(data)[1:33]
     hmac_computed = memoryview(HMAC(self.enc_hmac_key, memoryview(data)[33:], sha256).digest())
     if not compare_digest(hmac_computed, hmac_given):
         raise IntegrityError('Encryption envelope checksum mismatch')
     self.dec_cipher.reset(iv=PREFIX + data[33:41])
     data = self.compressor.decompress(self.dec_cipher.decrypt(data[41:]))
     if id:
         hmac_given = id
         hmac_computed = HMAC(self.id_key, data, sha256).digest()
         if not compare_digest(hmac_computed, hmac_given):
             raise IntegrityError('Chunk id verification failed')
     return data
def proxy_signature_is_valid(request, secret):
    """
    Return true if the calculated signature matches that present in the query string of the given request.
    """

    # Allow skipping of validation with an explicit setting.
    # If setting not present, skip if in debug mode by default.
    skip_validation = getattr(settings, 'SKIP_APP_PROXY_VALIDATION', settings.DEBUG)
    if skip_validation:
        return True

    # Create a mutable version of the GET parameters.
    query_dict = request.GET.copy()

    # Extract the signature we're going to verify. If no signature's present, the request is invalid.
    try:
        signature_to_verify = query_dict.pop('signature')[0]
    except KeyError:
        return False

    calculated_signature = get_proxy_signature(query_dict, secret)

    # Try to use compare_digest() to reduce vulnerability to timing attacks.
    # If it's not available, just fall back to regular string comparison.
    try:
        return hmac.compare_digest(calculated_signature.encode('utf-8'), signature_to_verify.encode('utf-8'))
    except AttributeError:
        return calculated_signature == signature_to_verify
Example #26
0
def githook(req: HttpRequest):

    # check User-agent
    if not req.META.get('HTTP_USER_AGENT', '').startswith('GitHub-Hookshot'):
        return HttpResponse('UA error')

    # check event
    if not req.META.get('HTTP_X_GITHUB_EVENT', '') == 'push':
        return HttpResponse('Event error')

    try:
        # check sha1 signature
        name, req_sha1 = req.META.get('HTTP_X_HUB_SIGNATURE', '').split('=')
        if name != 'sha1':
            return HttpResponse('Signature format error')
        HOOK_SECRET_KEY = get_object_or_404(GithubHookSecret, pk=1)
        data = req.read()
        mac = hmac.new(HOOK_SECRET_KEY.secret.encode(), data, digestmod=hashlib.sha1)
        if not hmac.compare_digest(mac.hexdigest(), req_sha1):
            return HttpResponse('Signature check error')

        # get payload
        payload = json.loads(data.decode('utf-8'), encoding='utf-8')

        # check branch
        if payload['ref'].endswith('master'):
            script = os.path.join(settings.BASE_DIR, 'deploy.sh')
            proc = subprocess.Popen([script], stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)
            return HttpResponse(proc.communicate()[0])
        else:
            return HttpResponse('Not master branch, ignore')
    except Exception as e:
        return HttpResponse(str(e))
Example #27
0
 def verify_password(self, user, password):
     if user in self.passwd:
         _pwd = self.passwd[user]
         if not hmac.compare_digest(_pwd.encode(), password.encode()):
             raise FailedAuthentication('Wrong user/password combination')
     else:
         raise FailedAuthentication('Wrong user/password combination')
    def async_validate_login(self, password: str) -> None:
        """Validate a username and password."""
        hass_http = getattr(self.hass, 'http', None)  # type: HomeAssistantHTTP

        if not hmac.compare_digest(hass_http.api_password.encode('utf-8'),
                                   password.encode('utf-8')):
            raise InvalidAuthError
Example #29
0
    def validate_signup_token(self, token):
        """
        Validate a signup token

        Parameters
        ----------
        token : str

        Returns
        -------
        username : str or None
            This will be None if the validation fails

        """
        if self.signing_key is None:
            return None
        pieces = token.split(":")
        signature = pieces.pop()
        username = pieces[0]
        issued = int(pieces[1])
        if issued + self.token_expiration < time.time():
            return None
        _, expected = self._hmac(username, issued)
        if hasattr(hmac, "compare_digest"):
            if isinstance(signature, six.text_type):
                signature = signature.encode("utf-8")
            if isinstance(expected, six.text_type):
                expected = expected.encode("utf-8")
            if not hmac.compare_digest(signature, expected):
                return None
        else:
            if signature != expected:
                return None
        return username
Example #30
0
def Decrypt(fileProtected, cipher, digest, key, IV):
    fileToDeProtect = fileProtected.split('.')[0] + "." + fileProtected.split('.')[1] + ".dec"
    try:
        if hmac.compare_digest(CheckHash(fileProtected, key, IV), digest) == True:
            with open(fileProtected, 'rb') as inFile, open(fileToDeProtect, 'wa') as outFile:
                cryptData = inFile.read()
                clearData = cipher.decrypt(cryptData)
                cleanData = encoder.decode(clearData)
                outFile.write(cleanData)
            inFile.close()
            outFile.close()
        else:
            raise ValueError ('Data Not Secure')
        SendData('############################ Decrption Success')
        SendData('--> original file: ' + fileProtected)
        SendData('--> protected file: ' + fileToDeProtect)
        SendData('HashKEY: ' + base64.b64encode(DKey(key, IV)))
        SendData('HASH: Verified')
        SendData('----------------------------------------------')
        SendData('END')
    except:
        SendData('############################ Decrption Failed')
        SendData('Removing files!')
        if os.path.isfile(fileProtected ) == True:
            os.remove(fileProtected)
            SendData('File protected deleted')
        if os.path.isfile(fileToDeProtect) == True:
            os.remove(fileToDeProtect)
            SendData('File to de-protect deleted')
        else:
            SendData('--> no file here')
            SendData('----------------------------------------------')
            SendData('END')
Example #31
0
 def check_hash(cls, hash_, plaintext):
     if len(hash_) != cls._hash_length * 2: return False
     import hmac
     return hmac.compare_digest(cls.get_hash(plaintext), hash_)
Example #32
0
def is_valid_signature(x_hub_signature, data, private_key):
    hash_algorithm, github_signature = x_hub_signature.split('=', 1)
    algorithm = hashlib.__dict__.get(hash_algorithm)
    encoded_key = bytes(private_key, 'latin-1')
    mac = hmac.new(encoded_key, msg=data, digestmod=algorithm)
    return hmac.compare_digest(mac.hexdigest(), github_signature)
Example #33
0
def CheckString(check):
    HM3 = hmac.new(SignKey, check[:-32], hashlib.sha256)
    if hmac.compare_digest(HM3.digest(), check[-32:]) == True:
        pass
    else:
        sys.exit(1)
 def is_assr_valid(self, key, assr):
     calc_assr = hmac.new(self.secret, key.encode("utf8"),
                          hashlib.sha224).digest()[:12]
     return hmac.compare_digest(assr, calc_assr)
Example #35
0
    def deserialize(
        self,
        msg_list: t.Union[t.List[bytes], t.List[zmq.Message]],
        content: bool = True,
        copy: bool = True,
    ) -> t.Dict[str, t.Any]:
        """Unserialize a msg_list to a nested message dict.

        This is roughly the inverse of serialize. The serialize/deserialize
        methods work with full message lists, whereas pack/unpack work with
        the individual message parts in the message list.

        Parameters
        ----------
        msg_list : list of bytes or Message objects
            The list of message parts of the form [HMAC,p_header,p_parent,
            p_metadata,p_content,buffer1,buffer2,...].
        content : bool (True)
            Whether to unpack the content dict (True), or leave it packed
            (False).
        copy : bool (True)
            Whether msg_list contains bytes (True) or the non-copying Message
            objects in each place (False).

        Returns
        -------
        msg : dict
            The nested message dict with top-level keys [header, parent_header,
            content, buffers].  The buffers are returned as memoryviews.
        """
        minlen = 5
        message = {}
        if not copy:
            # pyzmq didn't copy the first parts of the message, so we'll do it
            msg_list = t.cast(t.List[zmq.Message], msg_list)
            msg_list_beginning = [
                bytes(msg.bytes) for msg in msg_list[:minlen]
            ]  # type: ignore
            msg_list = t.cast(t.List[bytes], msg_list)
            msg_list = msg_list_beginning + msg_list[minlen:]
        msg_list = t.cast(t.List[bytes], msg_list)
        if self.auth is not None:
            signature = t.cast(bytes, msg_list[0])
            if not signature:
                raise ValueError("Unsigned Message")
            if signature in self.digest_history:
                raise ValueError("Duplicate Signature: %r" % signature)
            if content:
                # Only store signature if we are unpacking content, don't store if just peeking.
                self._add_digest(signature)
            check = self.sign(msg_list[1:5])
            if not compare_digest(signature, check):
                raise ValueError("Invalid Signature: %r" % signature)
        if not len(msg_list) >= minlen:
            raise TypeError(
                "malformed message, must have at least %i elements" % minlen)
        header = self.unpack(msg_list[1])
        message["header"] = extract_dates(header)
        message["msg_id"] = header["msg_id"]
        message["msg_type"] = header["msg_type"]
        message["parent_header"] = extract_dates(self.unpack(msg_list[2]))
        message["metadata"] = self.unpack(msg_list[3])
        if content:
            message["content"] = self.unpack(msg_list[4])
        else:
            message["content"] = msg_list[4]
        buffers = [memoryview(b) for b in msg_list[5:]]
        if buffers and buffers[0].shape is None:
            # force copy to workaround pyzmq #646
            msg_list = t.cast(t.List[zmq.Message], msg_list)
            buffers = [memoryview(bytes(b.bytes))
                       for b in msg_list[5:]]  # type: ignore
        message["buffers"] = buffers
        if self.debug:
            pprint.pprint(message)
        # adapt to the current version
        return adapt(message)
Example #36
0
def _verify_signature(payload, signature, secret):
    """Verify GitHub signature"""
    mac = hmac.new(secret, msg=payload, digestmod=hashlib.sha1)

    return hmac.compare_digest('sha1=' + mac.hexdigest(), signature)
Example #37
0
def constant_time_compare(val1, val2):
    """Return True if the two strings are equal, False otherwise."""
    return hmac.compare_digest(to_bytes(val1, 'utf8'), to_bytes(val2, 'utf8'))
Example #38
0
def index():
    """
    Main WSGI application entry.
    """

    path = normpath(abspath(dirname(__file__)))

    # Only POST is implemented
    if request.method != 'POST':
        abort(501)

    # Load config
    with open(join(path, 'config.json'), 'r') as cfg:
        config = loads(cfg.read())

    hooks = config.get('hooks_path', join(path, 'hooks'))

    # Allow Github IPs only
    if config.get('github_ips_only', True):
        src_ip = ip_address(
            u'{}'.format(request.access_route[0])  # Fix stupid ipaddress issue
        )
        whitelist = requests.get('https://api.github.com/meta').json()['hooks']

        for valid_ip in whitelist:
            if src_ip in ip_network(valid_ip):
                break
        else:
            logging.error('IP {} not allowed'.format(
                src_ip
            ))
            abort(403)

    # Enforce secret
    secret = config.get('enforce_secret', '')
    if secret:
        # Only SHA1 is supported
        header_signature = request.headers.get('X-Hub-Signature')
        if header_signature is None:
            abort(403)

        sha_name, signature = header_signature.split('=')
        if sha_name != 'sha1':
            abort(501)

        # HMAC requires the key to be bytes, but data is string
        mac = hmac.new(str(secret), msg=request.data, digestmod=sha1)

        # Python prior to 2.7.7 does not have hmac.compare_digest
        if hexversion >= 0x020707F0:
            if not hmac.compare_digest(str(mac.hexdigest()), str(signature)):
                abort(403)
        else:
            # What compare_digest provides is protection against timing
            # attacks; we can live without this protection for a web-based
            # application
            if not str(mac.hexdigest()) == str(signature):
                abort(403)

    # Implement ping
    event = request.headers.get('X-GitHub-Event', 'ping')
    if event == 'ping':
        return dumps({'msg': 'pong'})

    # Gather data
    try:
        payload = request.get_json()
    except Exception:
        logging.warning('Request parsing failed')
        abort(400)

    # Determining the branch is tricky, as it only appears for certain event
    # types an at different levels
    branch = None
    try:
        # Case 1: a ref_type indicates the type of ref.
        # This true for create and delete events.
        if 'ref_type' in payload:
            if payload['ref_type'] == 'branch':
                branch = payload['ref']

        # Case 2: a pull_request object is involved. This is pull_request and
        # pull_request_review_comment events.
        elif 'pull_request' in payload:
            # This is the TARGET branch for the pull-request, not the source
            # branch
            branch = payload['pull_request']['base']['ref']

        elif event in ['push']:
            # Push events provide a full Git ref in 'ref' and not a 'ref_type'.
            branch = payload['ref'].split('/', 2)[2]

    except KeyError:
        # If the payload structure isn't what we expect, we'll live without
        # the branch name
        pass

    # All current events have a repository, but some legacy events do not,
    # so let's be safe
    name = payload['repository']['name'] if 'repository' in payload else None

    meta = {
        'name': name,
        'branch': branch,
        'event': event
    }
    logging.info('Metadata:\n{}'.format(dumps(meta)))

    # Skip push-delete
    if event == 'push' and payload['deleted']:
        logging.info('Skipping push-delete event for {}'.format(dumps(meta)))
        return dumps({'status': 'skipped'})

    # Possible hooks
    scripts = []
    if branch and name:
        scripts.append(join(hooks, '{event}-{name}-{branch}'.format(**meta)))
    if name:
        scripts.append(join(hooks, '{event}-{name}'.format(**meta)))
    scripts.append(join(hooks, '{event}'.format(**meta)))
    scripts.append(join(hooks, 'all'))

    # Check permissions
    scripts = [s for s in scripts if isfile(s) and access(s, X_OK)]
    if not scripts:
        return dumps({'status': 'nop'})

    # Save payload to temporal file
    osfd, tmpfile = mkstemp()
    with fdopen(osfd, 'w') as pf:
        pf.write(dumps(payload))

    # Run scripts
    ran = {}
    for s in scripts:

        proc = Popen(
            [s, tmpfile, event],
            stdout=PIPE, stderr=PIPE
        )
        stdout, stderr = proc.communicate()

        ran[basename(s)] = {
            'returncode': proc.returncode,
            'stdout': stdout.decode('utf-8'),
            'stderr': stderr.decode('utf-8'),
        }

        # Log errors if a hook failed
        if proc.returncode != 0:
            logging.error('{} : {} \n{}'.format(
                s, proc.returncode, stderr
            ))

    # Remove temporal file
    remove(tmpfile)

    info = config.get('return_scripts_info', False)
    if not info:
        return dumps({'status': 'done'})

    output = dumps(ran, sort_keys=True, indent=4)
    logging.info(output)
    return output
Example #39
0
def compare(a, b):
    """Safely compare two values"""
    return hmac.compare_digest(a, b)
Example #40
0
 def verify(self, entity_id):
     """Check if the signature matches the current namespace."""
     entity_id, digest = self.parse(entity_id)
     if digest is None:
         return False
     return hmac.compare_digest(digest, self.signature(entity_id))
Example #41
0
def verify_plaintext(request):
    """Verify a PLAINTEXT signature."""
    sig = plaintext_signature(request.client_secret, request.token_secret)
    return hmac.compare_digest(sig, request.signature)
Example #42
0
def validate_security_token(security_token, variable):
    if hmac.compare_digest(security_token, variable):
        return True
    return False
Example #43
0
    async def on_POST(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
        self._clear_old_nonces()

        if not self.hs.config.registration.registration_shared_secret:
            raise SynapseError(HTTPStatus.BAD_REQUEST,
                               "Shared secret registration is not enabled")

        body = parse_json_object_from_request(request)

        if "nonce" not in body:
            raise SynapseError(
                HTTPStatus.BAD_REQUEST,
                "nonce must be specified",
                errcode=Codes.BAD_JSON,
            )

        nonce = body["nonce"]

        if nonce not in self.nonces:
            raise SynapseError(HTTPStatus.BAD_REQUEST, "unrecognised nonce")

        # Delete the nonce, so it can't be reused, even if it's invalid
        del self.nonces[nonce]

        if "username" not in body:
            raise SynapseError(
                HTTPStatus.BAD_REQUEST,
                "username must be specified",
                errcode=Codes.BAD_JSON,
            )
        else:
            if not isinstance(body["username"],
                              str) or len(body["username"]) > 512:
                raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username")

            username = body["username"].encode("utf-8")
            if b"\x00" in username:
                raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid username")

        if "password" not in body:
            raise SynapseError(
                HTTPStatus.BAD_REQUEST,
                "password must be specified",
                errcode=Codes.BAD_JSON,
            )
        else:
            password = body["password"]
            if not isinstance(password, str) or len(password) > 512:
                raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password")

            password_bytes = password.encode("utf-8")
            if b"\x00" in password_bytes:
                raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid password")

            password_hash = await self.auth_handler.hash(password)

        admin = body.get("admin", None)
        user_type = body.get("user_type", None)
        displayname = body.get("displayname", None)

        if user_type is not None and user_type not in UserTypes.ALL_USER_TYPES:
            raise SynapseError(HTTPStatus.BAD_REQUEST, "Invalid user type")

        if "mac" not in body:
            raise SynapseError(HTTPStatus.BAD_REQUEST,
                               "mac must be specified",
                               errcode=Codes.BAD_JSON)

        got_mac = body["mac"]

        want_mac_builder = hmac.new(
            key=self.hs.config.registration.registration_shared_secret.encode(
            ),
            digestmod=hashlib.sha1,
        )
        want_mac_builder.update(nonce.encode("utf8"))
        want_mac_builder.update(b"\x00")
        want_mac_builder.update(username)
        want_mac_builder.update(b"\x00")
        want_mac_builder.update(password_bytes)
        want_mac_builder.update(b"\x00")
        want_mac_builder.update(b"admin" if admin else b"notadmin")
        if user_type:
            want_mac_builder.update(b"\x00")
            want_mac_builder.update(user_type.encode("utf8"))

        want_mac = want_mac_builder.hexdigest()

        if not hmac.compare_digest(want_mac.encode("ascii"),
                                   got_mac.encode("ascii")):
            raise SynapseError(HTTPStatus.FORBIDDEN, "HMAC incorrect")

        # Reuse the parts of RegisterRestServlet to reduce code duplication
        from synapse.rest.client.register import RegisterRestServlet

        register = RegisterRestServlet(self.hs)

        user_id = await register.registration_handler.register_user(
            localpart=body["username"].lower(),
            password_hash=password_hash,
            admin=bool(admin),
            user_type=user_type,
            default_display_name=displayname,
            by_admin=True,
        )

        result = await register._create_registration_details(user_id, body)
        return HTTPStatus.OK, result
Example #44
0
def verify_hmac_sha1(request):
    """Verify a HMAC-SHA1 signature."""
    base_string = generate_signature_base_string(request)
    sig = hmac_sha1_signature(base_string, request.client_secret,
                              request.token_secret)
    return hmac.compare_digest(sig, request.signature)
Example #45
0
def mac_verify(key, data, mac):
    computed_mac = mac_generate(key, data)
    result = hmac.compare_digest(mac.upper(), computed_mac.upper())
    log.info("MAC of %r seems to be %r. Expected %r (%r)", data[:20],
             computed_mac[:20], mac[:20], result)
    return result
Example #46
0
def _verify_hash(signature, s, alg):
    hash_value = create_half_hash(s, alg)
    if not hash_value:
        return True
    return hmac.compare_digest(hash_value, to_bytes(signature))
Example #47
0
 def autorize_user(self, message, sock):
     # Если имя пользователя уже занято то возвращаем 400
     if message[USER][ACCOUNT_NAME] in self.names.keys():
         response = RESPONSE_400
         response[ERROR] = 'Имя пользователя уже занято.'
         try:
             send_message(sock, response)
         except OSError:
             pass
         self.clients.remove(sock)
         sock.close()
     # Проверяем что пользователь зарегистрирован на сервере.
     elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
         response = RESPONSE_400
         response[ERROR] = 'Пользователь не зарегистрирован.'
         try:
             send_message(sock, response)
         except OSError:
             pass
         self.clients.remove(sock)
         sock.close()
     else:
         # Иначе отвечаем 501 и проводим процедуру авторизации
         # Словарь - заготовка
         message_auth = RESPONSE_511
         # Набор байтов в hex представлении
         random_str = binascii.hexlify(os.urandom(64))
         # В словарь байты нельзя, декодируем (json.dumps -> TypeError)
         message_auth[DATA] = random_str.decode('ascii')
         # Создаём хэш пароля и связки с рандомной строкой, сохраняем серверную версию ключа
         hash = hmac.new(self.database.get_hash(message[USER][ACCOUNT_NAME]), random_str)
         digest = hash.digest()
         try:
             # Обмен с клиентом
             send_message(sock, message_auth)
             ans = get_message(sock)
         except OSError:
             sock.close()
             return
         client_digest = binascii.a2b_base64(ans[DATA])
         # Если ответ клиента корректный, то сохраняем его в список пользователей.
         if RESPONSE in ans and ans[RESPONSE] == 511 and hmac.compare_digest(digest, client_digest):
             self.names[message[USER][ACCOUNT_NAME]] = sock
             client_ip, client_port = sock.getpeername()
             try:
                 send_message(sock, RESPONSE_200)
             except OSError:
                 self.remove_client(message[USER][ACCOUNT_NAME])
             # добавляем пользователя в список активных и если у него изменился открытый ключ
             # сохраняем новый
             self.database.user_login(message[USER][ACCOUNT_NAME],
                                      client_ip, client_port, message[USER][PUBLIC_KEY])
         else:
             response = RESPONSE_400
             response[ERROR] = 'Неверный пароль.'
             try:
                 send_message(sock, response)
             except OSError:
                 pass
             self.clients.remove(sock)
             sock.close()
Example #48
0
def valid_signature(headers, payload, secret):
    encoding = 'utf-8'
    algo, sig = headers.get('X-Hub-Signature').split('=')
    digest = hmac.new(secret.encode(encoding), payload.encode(encoding),
                      getattr(hashlib, algo)).hexdigest()
    return hmac.compare_digest(sig.encode(encoding), digest.encode(encoding))
Example #49
0
 def assert_id(self, id, data):
     if id:
         id_computed = self.id_hash(data)
         if not compare_digest(id_computed, id):
             raise IntegrityError('Chunk %s: id verification failed' %
                                  bin_to_hex(id))
Example #50
0
def compare(d1, d2):
    return hmac.compare_digest(_force_bytes(d1), _force_bytes(d2))
Example #51
0
def verify_encrypted(encrypted, text):
    """
    Return True/False if the encrypted content corresponds to the unencrypted text.
    """
    return compare_digest(crypt(text, encrypted), encrypted)
Example #52
0
    def deserialize(self, msg_list, content=True, copy=True):
        """Unserialize a msg_list to a nested message dict.

        This is roughly the inverse of serialize. The serialize/deserialize
        methods work with full message lists, whereas pack/unpack work with
        the individual message parts in the message list.

        Parameters
        ----------
        msg_list : list of bytes or Message objects
            The list of message parts of the form [HMAC,p_header,p_parent,
            p_metadata,p_content,buffer1,buffer2,...].
        content : bool (True)
            Whether to unpack the content dict (True), or leave it packed
            (False).
        copy : bool (True)
            Whether msg_list contains bytes (True) or the non-copying Message
            objects in each place (False).

        Returns
        -------
        msg : dict
            The nested message dict with top-level keys [header, parent_header,
            content, buffers].  The buffers are returned as memoryviews.
        """
        minlen = 5
        message = {}
        if not copy:
            # pyzmq didn't copy the first parts of the message, so we'll do it
            for i in range(minlen):
                msg_list[i] = msg_list[i].bytes
        if self.auth is not None:
            signature = msg_list[0]
            if not signature:
                raise ValueError("Unsigned Message")
            if signature in self.digest_history:
                raise ValueError("Duplicate Signature: %r" % signature)
            if content:
                # Only store signature if we are unpacking content, don't store if just peeking.
                self._add_digest(signature)
            check = self.sign(msg_list[1:5])
            if not compare_digest(signature, check):
                raise ValueError("Invalid Signature: %r" % signature)
        if not len(msg_list) >= minlen:
            raise TypeError(
                "malformed message, must have at least %i elements" % minlen)
        header = self.unpack(msg_list[1])
        message['header'] = extract_dates(header)
        message['msg_id'] = header['msg_id']
        message['msg_type'] = header['msg_type']
        message['parent_header'] = extract_dates(self.unpack(msg_list[2]))
        message['metadata'] = self.unpack(msg_list[3])
        if content:
            message['content'] = self.unpack(msg_list[4])
        else:
            message['content'] = msg_list[4]
        buffers = [memoryview(b) for b in msg_list[5:]]
        if buffers and buffers[0].shape is None:
            # force copy to workaround pyzmq #646
            buffers = [memoryview(b.bytes) for b in msg_list[5:]]
        message['buffers'] = buffers
        if self.debug:
            pprint.pprint(message)
        cando_log("<<< deserialize", Session.session_log, message,
                  Session.session_deserialize)
        # adapt to the current version
        return adapt(message)
def _authenticate_scram(credentials, sock_info, mechanism):
    """Authenticate using SCRAM."""

    username = credentials.username
    if mechanism == 'SCRAM-SHA-256':
        digest = "sha256"
        digestmod = hashlib.sha256
        data = saslprep(credentials.password).encode("utf-8")
    else:
        digest = "sha1"
        digestmod = hashlib.sha1
        data = _password_digest(username, credentials.password).encode("utf-8")
    source = credentials.source
    cache = credentials.cache

    # Make local
    _hmac = hmac.HMAC

    user = username.encode("utf-8").replace(b"=", b"=3D").replace(b",", b"=2C")
    nonce = standard_b64encode(os.urandom(32))
    first_bare = b"n=" + user + b",r=" + nonce

    cmd = SON([('saslStart', 1), ('mechanism', mechanism),
               ('payload', Binary(b"n,," + first_bare)), ('autoAuthorize', 1),
               ('options', {
                   'skipEmptyExchange': True
               })])
    res = sock_info.command(source, cmd)

    server_first = res['payload']
    parsed = _parse_scram_response(server_first)
    iterations = int(parsed[b'i'])
    if iterations < 4096:
        raise OperationFailure("Server returned an invalid iteration count.")
    salt = parsed[b's']
    rnonce = parsed[b'r']
    if not rnonce.startswith(nonce):
        raise OperationFailure("Server returned an invalid nonce.")

    without_proof = b"c=biws,r=" + rnonce
    if cache.data:
        client_key, server_key, csalt, citerations = cache.data
    else:
        client_key, server_key, csalt, citerations = None, None, None, None

    # Salt and / or iterations could change for a number of different
    # reasons. Either changing invalidates the cache.
    if not client_key or salt != csalt or iterations != citerations:
        salted_pass = _hi(digest, data, standard_b64decode(salt), iterations)
        client_key = _hmac(salted_pass, b"Client Key", digestmod).digest()
        server_key = _hmac(salted_pass, b"Server Key", digestmod).digest()
        cache.data = (client_key, server_key, salt, iterations)
    stored_key = digestmod(client_key).digest()
    auth_msg = b",".join((first_bare, server_first, without_proof))
    client_sig = _hmac(stored_key, auth_msg, digestmod).digest()
    client_proof = b"p=" + standard_b64encode(_xor(client_key, client_sig))
    client_final = b",".join((without_proof, client_proof))

    server_sig = standard_b64encode(
        _hmac(server_key, auth_msg, digestmod).digest())

    cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']),
               ('payload', Binary(client_final))])
    res = sock_info.command(source, cmd)

    parsed = _parse_scram_response(res['payload'])
    if not compare_digest(parsed[b'v'], server_sig):
        raise OperationFailure("Server returned an invalid signature.")

    # A third empty challenge may be required if the server does not support
    # skipEmptyExchange: SERVER-44857.
    if not res['done']:
        cmd = SON([('saslContinue', 1),
                   ('conversationId', res['conversationId']),
                   ('payload', Binary(b''))])
        res = sock_info.command(source, cmd)
        if not res['done']:
            raise OperationFailure('SASL conversation failed to complete.')
Example #54
0
def webhook():
    json = request.json

    # Get package
    github_url = "github.com/" + json["repository"]["full_name"]
    package = Package.query.filter(
        Package.repo.ilike("%{}%".format(github_url))).first()
    if package is None:
        return error(
            400,
            "Could not find package, did you set the VCS repo in CDB correctly? Expected {}"
            .format(github_url))

    # Get all tokens for package
    tokens_query = APIToken.query.filter(
        or_(APIToken.package == package,
            and_(APIToken.package == None, APIToken.owner == package.author)))

    possible_tokens = tokens_query.all()
    actual_token = None

    #
    # Check signature
    #

    header_signature = request.headers.get('X-Hub-Signature')
    if header_signature is None:
        return error(403, "Expected payload signature")

    sha_name, signature = header_signature.split('=')
    if sha_name != 'sha1':
        return error(403, "Expected SHA1 payload signature")

    for token in possible_tokens:
        mac = hmac.new(token.access_token.encode("utf-8"),
                       msg=request.data,
                       digestmod='sha1')

        if hmac.compare_digest(str(mac.hexdigest()), signature):
            actual_token = token
            break

    if actual_token is None:
        return error(403,
                     "Invalid authentication, couldn't validate API token")

    if not package.checkPerm(actual_token.owner, Permission.APPROVE_RELEASE):
        return error(403, "You do not have the permission to approve releases")

    #
    # Check event
    #

    event = request.headers.get("X-GitHub-Event")
    if event == "push":
        ref = json["after"]
        title = json["head_commit"]["message"].partition("\n")[0]
    elif event == "create" and json["ref_type"] == "tag":
        ref = json["ref"]
        title = ref
    elif event == "ping":
        return jsonify({"success": True, "message": "Ping successful"})
    else:
        return error(
            400,
            "Unsupported event. Only 'push', `create:tag`, and 'ping' are supported."
        )

    #
    # Perform release
    #

    return handleCreateRelease(actual_token, package, title, ref)
Example #55
0
def slack_webhook(request):
    logger = logging.getLogger(__name__)
    logger.error("---------------- Slack Webhook -------------- ")

    logger.error(
        f"---------------- request.META: {request.META} -------------- ")

    # Get HMAC from request
    timestamp, hmac_digest = get_slack_hmac(request)
    slack_signature = request.META["HTTP_X_SLACK_SIGNATURE"]

    # The request timestamp is more than ten seconds from local time.
    # If so, we ignore it.
    occurred_in_last_10s = (int(datetime.datetime.now().timestamp()) -
                            int(timestamp)) < 10

    # If signatures don't match, or request occurred more than 5 minutes ago, discard request
    if not (hmac.compare_digest(slack_signature, hmac_digest)
            and occurred_in_last_10s):
        logger.error(f"slack_signature: {slack_signature}")
        logger.error(f"hmac_digest: {hmac_digest}")
        logger.error(f"occurred_in_last_10s: {occurred_in_last_10s}")
        capture_message(
            f"Old webhook or Invalid signature from Slack: discarding.  request.body: {request.body}. slack_sig: {slack_signature}. computed_sig: {hmac_digest}. occurred_in_last_10s: {occurred_in_last_10s}."
        )
        return HttpResponse(
            status=200)  # Return a 200 so Slack doesn't keep retrying

    logger.error(
        "---------------- After invalid signature guard -------------- ")

    # Slack needs to verify the URL and does so by sending a "challenge" param when we set the URL in their
    # GUI here: https://api.slack.com/apps/AHB04HNE9/event-subscriptions
    # A payload with "challenge" is only sent when we set a new webhook URL
    if "challenge" in request.data:
        return JsonResponse({"challenge": request.data["challenge"]})

    logger.error("---------------- After challenge guard -------------- ")

    # Handle app_uninstalled webhooks

    if request.data["event"]["type"] == "app_uninstalled":
        team_id = request.data["team_id"]
        try:
            # TODO: pretty sure we have a bug here. If you have multiple installs
            # for the same team it's going to generate a MutipleObjects exception.
            # We should probably just being doing filter(slack_team_id=team_id).delete().
            # Don't have time to dig in on that right now.
            slack_settings = SlackSettings.objects.get(slack_team_id=team_id)
        except SlackSettings.DoesNotExist:
            return HttpResponse(status=200)

        logger.error("---------------- Uninstalled Slack app -------------- ")

        tracking.integration_disconnected(slack_settings.user,
                                          tracking.EVENT_SOURCE_SLACK)
        slack_settings.delete()

        return HttpResponse(status=200)

    logger.error(
        "---------------- After uninstall webhook guard -------------- ")

    channel_id = request.data["event"]["channel"]
    team_id = request.data["team_id"]

    # This section responds to a DM to SavioBot
    if request.data["event"]["channel_type"] == "im":

        # Ignore all requests with a bot_id in them - we don't want to respond to ourself!
        if "bot_id" in request.data["event"]:
            return HttpResponse(status=200)

        # This is a DM, so get the customer by team_id.
        try:
            slack_settings = SlackSettings.objects.get(
                slack_team_id=team_id, slack_feedback_channel_id=channel_id)
        except SlackSettings.DoesNotExist:
            return HttpResponse(status=406)

        headers = {
            "Content-Type": "application/json; charset=utf-8",
            "Authorization": "Bearer " + slack_settings.slack_bot_token,
        }
        msg_url = "https://slack.com/api/chat.postMessage"

        if request.data["event"]["text"] == "help":
            text = "I help you send customer feedback posted in Slack to Savio.\n\n"
            text = (
                text +
                f"There are two ways to use Savio:\n\n1. Post customer feedback to the #{slack_settings.slack_feedback_channel_name} channel and I'll ask you if you want to send it to Savio. <https://www.youtube.com/watch?v=KOwnybk_clU|Watch a 30 second video.>"
            )
            text = (
                text +
                "\n2. Click the three dots to the right of any Slack message and choose 'Push to Savio'.  <http://www.youtube.com/watch?v=DY7Ci5kUVG8|Watch a 30 second video.>"
            )
            msg_json = {
                "channel": channel_id,
                "text": text,
                "link_names": True,
                "unfurl_media": False,
            }
        elif request.data["event"]["text"] == "power":
            text = f"Send feedback to Savio faster when you post Slack messages to #{slack_settings.slack_feedback_channel_name} by using this format:\n"
            text = (
                text +
                "1. `[email protected]: Some feedback from your customer` OR \n2. `Customer Name: Some feedback from your customer`"
            )
            text = (
                text +
                f"\n\nWhen you use this format, we'll populate the Person dropdown with that person if they've been imported into Savio.\n\nWatch it in action: {settings.HOST}/static/images/help/slack-power-user.gif"
            )
            msg_json = {
                "channel": channel_id,
                "text": text,
                "link_names": True,
                "unfurl_link": True,
                "unfurl_media": True,
            }
        else:
            msg_json = {
                "channel":
                channel_id,
                "text":
                "Sorry, I don't understand that. Please type 'help' or 'power' if you're a power user.",
            }

        requests.post(msg_url, data=json.dumps(msg_json), headers=headers)

    elif "user" in request.data["event"]:

        logger.error(
            "---------------- Not a DM, is Savio listing to channel? -------------- "
        )

        # This is not a DM, so get the customer by channel_id to see if we care about messages posted to this channel.
        if request.data["event"]["type"] == "message":
            try:
                slack_settings = SlackSettings.objects.get(
                    slack_team_id=team_id,
                    slack_feedback_channel_id=channel_id)
            except SlackSettings.DoesNotExist:
                logger.error(
                    f"---------------- Slack Settings does not exist for team_id {team_id} and slack_feedback_channel_id {channel_id}, returning 200 -------------- "
                )
                return HttpResponse(status=200)

        logger.error(
            f"---------------- Message is user initiated. Slack_settings: {slack_settings} -------------- "
        )

        # We have a user, which means this is a user-initiated message
        user_id = request.data["event"]["user"]

        logger.error(
            f"---------------- user_id == slack_settings.slack_bot_user_id: {user_id == slack_settings.slack_bot_user_id} -------------- "
        )
        logger.error(
            f"---------------- request.data: {request.data} -------------- ")

        # Don't respond with ephemeral msg if user is Slack bot or if msg is
        # part of a thread or if there's a message subtype - we don't care about those.
        if (not (user_id == slack_settings.slack_bot_user_id)
                and ("thread_ts" not in request.data["event"])
                and ("subtype" not in request.data["event"])):

            logger.error(
                "---------------- Inside Guard. Should return ephemeral message -------------- "
            )

            message_ts = request.data["event"]["ts"]

            headers = {
                "Content-Type": "application/json; charset=utf-8",
                "Authorization": "Bearer " + slack_settings.slack_bot_token,
            }
            msg_url = "https://slack.com/api/chat.postEphemeral"

            msg_json = {
                "channel":
                channel_id,
                "user":
                user_id,
                "as_user":
                False,
                "text":
                "",
                "blocks": [
                    {
                        "type": "section",
                        "text": {
                            "type":
                            "mrkdwn",
                            "text":
                            "Is this customer feedback that you want to send to Savio?",
                        },
                    },
                    {
                        "type":
                        "actions",
                        "elements": [
                            {
                                "type": "button",
                                "text": {
                                    "type": "plain_text",
                                    "text": "Yes"
                                },
                                "value": message_ts,
                            },
                            {
                                "type": "button",
                                "text": {
                                    "type": "plain_text",
                                    "text": "No"
                                },
                                "value": "No",
                            },
                        ],
                    },
                ],
            }

            requests.post(msg_url, data=json.dumps(msg_json), headers=headers)
        else:
            logger.error(
                "---------------- NOT inside Guard. NOT returning ephemeral message -------------- "
            )

    return Response(status=status.HTTP_200_OK)
Example #56
0
def file_upload(request):
    # If we're in read-only mode, let upload clients know
    if request.flags.enabled("read-only"):
        raise _exc_with_message(
            HTTPForbidden, "Read-only mode: Uploads are temporarily disabled")

    # Log an attempt to upload
    metrics = request.find_service(IMetricsService, context=None)
    metrics.increment("warehouse.upload.attempt")

    # Before we do anything, if there isn't an authenticated user with this
    # request, then we'll go ahead and bomb out.
    if request.authenticated_userid is None:
        raise _exc_with_message(
            HTTPForbidden,
            "Invalid or non-existent authentication information.")

    # Ensure that user has a verified, primary email address. This should both
    # reduce the ease of spam account creation and activity, as well as act as
    # a forcing function for https://github.com/pypa/warehouse/issues/3632.
    # TODO: Once https://github.com/pypa/warehouse/issues/3632 has been solved,
    #       we might consider a different condition, possibly looking at
    #       User.is_active instead.
    if not (request.user.primary_email
            and request.user.primary_email.verified):
        raise _exc_with_message(
            HTTPBadRequest,
            ("User {!r} does not have a verified primary email address. "
             "Please add a verified primary email before attempting to "
             "upload to PyPI. See {project_help} for more information."
             "for more information.").format(
                 request.user.username,
                 project_help=request.help_url(_anchor="verified-email"),
             ),
        ) from None

    # Do some cleanup of the various form fields
    for key in list(request.POST):
        value = request.POST.get(key)
        if isinstance(value, str):
            # distutils "helpfully" substitutes unknown, but "required" values
            # with the string "UNKNOWN". This is basically never what anyone
            # actually wants so we'll just go ahead and delete anything whose
            # value is UNKNOWN.
            if value.strip() == "UNKNOWN":
                del request.POST[key]

            # Escape NUL characters, which psycopg doesn't like
            if "\x00" in value:
                request.POST[key] = value.replace("\x00", "\\x00")

    # We require protocol_version 1, it's the only supported version however
    # passing a different version should raise an error.
    if request.POST.get("protocol_version", "1") != "1":
        raise _exc_with_message(HTTPBadRequest, "Unknown protocol version.")

    # Check if any fields were supplied as a tuple and have become a
    # FieldStorage. The 'content' and 'gpg_signature' fields _should_ be a
    # FieldStorage, however.
    # ref: https://github.com/pypa/warehouse/issues/2185
    # ref: https://github.com/pypa/warehouse/issues/2491
    for field in set(request.POST) - {"content", "gpg_signature"}:
        values = request.POST.getall(field)
        if any(isinstance(value, FieldStorage) for value in values):
            raise _exc_with_message(HTTPBadRequest,
                                    f"{field}: Should not be a tuple.")

    # Look up all of the valid classifiers
    all_classifiers = request.db.query(Classifier).all()

    # Validate and process the incoming metadata.
    form = MetadataForm(request.POST)

    # Add a validator for deprecated classifiers
    form.classifiers.validators.append(_no_deprecated_classifiers(request))

    form.classifiers.choices = [(c.classifier, c.classifier)
                                for c in all_classifiers]
    if not form.validate():
        for field_name in _error_message_order:
            if field_name in form.errors:
                break
        else:
            field_name = sorted(form.errors.keys())[0]

        if field_name in form:
            field = form[field_name]
            if field.description and isinstance(field, wtforms.StringField):
                error_message = (
                    "{value!r} is an invalid value for {field}. ".format(
                        value=field.data, field=field.description) +
                    "Error: {} ".format(form.errors[field_name][0]) + "See "
                    "https://packaging.python.org/specifications/core-metadata"
                )
            else:
                error_message = "Invalid value for {field}. Error: {msgs[0]}".format(
                    field=field_name, msgs=form.errors[field_name])
        else:
            error_message = "Error: {}".format(form.errors[field_name][0])

        raise _exc_with_message(HTTPBadRequest, error_message)

    # Ensure that we have file data in the request.
    if "content" not in request.POST:
        raise _exc_with_message(HTTPBadRequest,
                                "Upload payload does not have a file.")

    # Look up the project first before doing anything else, this is so we can
    # automatically register it if we need to and can check permissions before
    # going any further.
    try:
        project = (request.db.query(Project).filter(
            Project.normalized_name == func.normalize_pep426_name(
                form.name.data)).one())
    except NoResultFound:
        # Check for AdminFlag set by a PyPI Administrator disabling new project
        # registration, reasons for this include Spammers, security
        # vulnerabilities, or just wanting to be lazy and not worry ;)
        if request.flags.enabled("disallow-new-project-registration"):
            raise _exc_with_message(
                HTTPForbidden,
                ("New project registration temporarily disabled. "
                 "See {projecthelp} for details").format(
                     projecthelp=request.help_url(
                         _anchor="admin-intervention")),
            ) from None

        # Before we create the project, we're going to check our blacklist to
        # see if this project is even allowed to be registered. If it is not,
        # then we're going to deny the request to create this project.
        if request.db.query(exists().where(
                BlacklistedProject.name == func.normalize_pep426_name(
                    form.name.data))).scalar():
            raise _exc_with_message(
                HTTPBadRequest,
                ("The name {name!r} isn't allowed. "
                 "See {projecthelp} "
                 "for more information.").format(
                     name=form.name.data,
                     projecthelp=request.help_url(_anchor="project-name"),
                 ),
            ) from None

        # Also check for collisions with Python Standard Library modules.
        if packaging.utils.canonicalize_name(
                form.name.data) in STDLIB_PROHIBITTED:
            raise _exc_with_message(
                HTTPBadRequest,
                ("The name {name!r} isn't allowed (conflict with Python "
                 "Standard Library module name). See "
                 "{projecthelp} for more information.").format(
                     name=form.name.data,
                     projecthelp=request.help_url(_anchor="project-name"),
                 ),
            ) from None

        # The project doesn't exist in our database, so first we'll check for
        # projects with a similar name
        squattees = (request.db.query(Project).filter(
            func.levenshtein(Project.normalized_name,
                             func.normalize_pep426_name(form.name.data)) <= 2).
                     all())

        # Next we'll create the project
        project = Project(name=form.name.data)
        request.db.add(project)

        # Now that the project exists, add any squats which it is the squatter for
        for squattee in squattees:
            request.db.add(Squat(squatter=project, squattee=squattee))

        # Then we'll add a role setting the current user as the "Owner" of the
        # project.
        request.db.add(
            Role(user=request.user, project=project, role_name="Owner"))
        # TODO: This should be handled by some sort of database trigger or a
        #       SQLAlchemy hook or the like instead of doing it inline in this
        #       view.
        request.db.add(
            JournalEntry(
                name=project.name,
                action="create",
                submitted_by=request.user,
                submitted_from=request.remote_addr,
            ))
        request.db.add(
            JournalEntry(
                name=project.name,
                action="add Owner {}".format(request.user.username),
                submitted_by=request.user,
                submitted_from=request.remote_addr,
            ))

    # Check that the user has permission to do things to this project, if this
    # is a new project this will act as a sanity check for the role we just
    # added above.
    if not request.has_permission("upload", project):
        raise _exc_with_message(
            HTTPForbidden,
            ("The user '{0}' isn't allowed to upload to project '{1}'. "
             "See {2} for more information.").format(
                 request.user.username,
                 project.name,
                 request.help_url(_anchor="project-name"),
             ),
        )

    # Update name if it differs but is still equivalent. We don't need to check if
    # they are equivalent when normalized because that's already been done when we
    # queried for the project.
    if project.name != form.name.data:
        project.name = form.name.data

    # Render our description so we can save from having to render this data every time
    # we load a project description page.
    rendered = None
    if form.description.data:
        description_content_type = form.description_content_type.data
        if not description_content_type:
            description_content_type = "text/x-rst"

        rendered = readme.render(form.description.data,
                                 description_content_type,
                                 use_fallback=False)

        # Uploading should prevent broken rendered descriptions.
        if rendered is None:
            if form.description_content_type.data:
                message = (
                    "The description failed to render "
                    "for '{description_content_type}'.").format(
                        description_content_type=description_content_type)
            else:
                message = ("The description failed to render "
                           "in the default format of reStructuredText.")
            raise _exc_with_message(
                HTTPBadRequest,
                "{message} See {projecthelp} for more information.".format(
                    message=message,
                    projecthelp=request.help_url(
                        _anchor="description-content-type"),
                ),
            ) from None

    try:
        canonical_version = packaging.utils.canonicalize_version(
            form.version.data)
        release = (request.db.query(Release).filter(
            (Release.project == project)
            & (Release.canonical_version == canonical_version)).one())
    except MultipleResultsFound:
        # There are multiple releases of this project which have the same
        # canonical version that were uploaded before we checked for
        # canonical version equivalence, so return the exact match instead
        release = (request.db.query(
            Release).filter((Release.project == project)
                            & (Release.version == form.version.data)).one())
    except NoResultFound:
        release = Release(
            project=project,
            _classifiers=[
                c for c in all_classifiers
                if c.classifier in form.classifiers.data
            ],
            dependencies=list(
                _construct_dependencies(
                    form,
                    {
                        "requires": DependencyKind.requires,
                        "provides": DependencyKind.provides,
                        "obsoletes": DependencyKind.obsoletes,
                        "requires_dist": DependencyKind.requires_dist,
                        "provides_dist": DependencyKind.provides_dist,
                        "obsoletes_dist": DependencyKind.obsoletes_dist,
                        "requires_external": DependencyKind.requires_external,
                        "project_urls": DependencyKind.project_url,
                    },
                )),
            canonical_version=canonical_version,
            description=Description(
                content_type=form.description_content_type.data,
                raw=form.description.data or "",
                html=rendered or "",
                rendered_by=readme.renderer_version(),
            ),
            **{
                k: getattr(form, k).data
                for k in {
                    # This is a list of all the fields in the form that we
                    # should pull off and insert into our new release.
                    "version",
                    "summary",
                    "license",
                    "author",
                    "author_email",
                    "maintainer",
                    "maintainer_email",
                    "keywords",
                    "platform",
                    "home_page",
                    "download_url",
                    "requires_python",
                }
            },
            uploader=request.user,
            uploaded_via=request.user_agent,
        )
        request.db.add(release)
        # TODO: This should be handled by some sort of database trigger or
        #       a SQLAlchemy hook or the like instead of doing it inline in
        #       this view.
        request.db.add(
            JournalEntry(
                name=release.project.name,
                version=release.version,
                action="new release",
                submitted_by=request.user,
                submitted_from=request.remote_addr,
            ))

    # TODO: We need a better solution to this than to just do it inline inside
    #       this method. Ideally the version field would just be sortable, but
    #       at least this should be some sort of hook or trigger.
    releases = (request.db.query(Release).filter(
        Release.project == project).options(
            orm.load_only(Release._pypi_ordering)).all())
    for i, r in enumerate(
            sorted(releases,
                   key=lambda x: packaging.version.parse(x.version))):
        r._pypi_ordering = i

    # Pull the filename out of our POST data.
    filename = request.POST["content"].filename

    # Make sure that the filename does not contain any path separators.
    if "/" in filename or "\\" in filename:
        raise _exc_with_message(
            HTTPBadRequest,
            "Cannot upload a file with '/' or '\\' in the name.")

    # Make sure the filename ends with an allowed extension.
    if _dist_file_regexes[project.allow_legacy_files].search(filename) is None:
        raise _exc_with_message(
            HTTPBadRequest,
            "Invalid file extension: Use .egg, .tar.gz, .whl or .zip "
            "extension. (https://www.python.org/dev/peps/pep-0527)",
        )

    # Make sure that our filename matches the project that it is being uploaded
    # to.
    prefix = pkg_resources.safe_name(project.name).lower()
    if not pkg_resources.safe_name(filename).lower().startswith(prefix):
        raise _exc_with_message(
            HTTPBadRequest,
            "Start filename for {!r} with {!r}.".format(project.name, prefix),
        )

    # Check the content type of what is being uploaded
    if not request.POST["content"].type or request.POST[
            "content"].type.startswith("image/"):
        raise _exc_with_message(HTTPBadRequest, "Invalid distribution file.")

    # Ensure that the package filetype is allowed.
    # TODO: Once PEP 527 is completely implemented we should be able to delete
    #       this and just move it into the form itself.
    if not project.allow_legacy_files and form.filetype.data not in {
            "sdist",
            "bdist_wheel",
            "bdist_egg",
    }:
        raise _exc_with_message(HTTPBadRequest, "Unknown type of file.")

    # The project may or may not have a file size specified on the project, if
    # it does then it may or may not be smaller or larger than our global file
    # size limits.
    file_size_limit = max(filter(None, [MAX_FILESIZE, project.upload_limit]))

    with tempfile.TemporaryDirectory() as tmpdir:
        temporary_filename = os.path.join(tmpdir, filename)

        # Buffer the entire file onto disk, checking the hash of the file as we
        # go along.
        with open(temporary_filename, "wb") as fp:
            file_size = 0
            file_hashes = {
                "md5": hashlib.md5(),
                "sha256": hashlib.sha256(),
                "blake2_256": hashlib.blake2b(digest_size=256 // 8),
            }
            for chunk in iter(lambda: request.POST["content"].file.read(8096),
                              b""):
                file_size += len(chunk)
                if file_size > file_size_limit:
                    raise _exc_with_message(
                        HTTPBadRequest,
                        "File too large. " +
                        "Limit for project {name!r} is {limit} MB. ".format(
                            name=project.name,
                            limit=file_size_limit // (1024 * 1024)) + "See " +
                        request.help_url(_anchor="file-size-limit"),
                    )
                fp.write(chunk)
                for hasher in file_hashes.values():
                    hasher.update(chunk)

        # Take our hash functions and compute the final hashes for them now.
        file_hashes = {
            k: h.hexdigest().lower()
            for k, h in file_hashes.items()
        }

        # Actually verify the digests that we've gotten. We're going to use
        # hmac.compare_digest even though we probably don't actually need to
        # because it's better safe than sorry. In the case of multiple digests
        # we expect them all to be given.
        if not all([
                hmac.compare_digest(
                    getattr(form,
                            "{}_digest".format(digest_name)).data.lower(),
                    digest_value,
                ) for digest_name, digest_value in file_hashes.items()
                if getattr(form, "{}_digest".format(digest_name)).data
        ]):
            raise _exc_with_message(
                HTTPBadRequest,
                "The digest supplied does not match a digest calculated "
                "from the uploaded file.",
            )

        # Check to see if the file that was uploaded exists already or not.
        is_duplicate = _is_duplicate_file(request.db, filename, file_hashes)
        if is_duplicate:
            return Response()
        elif is_duplicate is not None:
            raise _exc_with_message(
                HTTPBadRequest,
                # Note: Changing this error message to something that doesn't
                # start with "File already exists" will break the
                # --skip-existing functionality in twine
                # ref: https://github.com/pypa/warehouse/issues/3482
                # ref: https://github.com/pypa/twine/issues/332
                "File already exists. See " +
                request.help_url(_anchor="file-name-reuse"),
            )

        # Check to see if the file that was uploaded exists in our filename log
        if request.db.query(
                request.db.query(Filename).filter(
                    Filename.filename == filename).exists()).scalar():
            raise _exc_with_message(
                HTTPBadRequest,
                "This filename has already been used, use a "
                "different version. "
                "See " + request.help_url(_anchor="file-name-reuse"),
            )

        # Check to see if uploading this file would create a duplicate sdist
        # for the current release.
        if (form.filetype.data == "sdist" and request.db.query(
                request.db.query(File).filter((File.release == release) & (
                    File.packagetype == "sdist")).exists()).scalar()):
            raise _exc_with_message(
                HTTPBadRequest, "Only one sdist may be uploaded per release.")

        # Check the file to make sure it is a valid distribution file.
        if not _is_valid_dist_file(temporary_filename, form.filetype.data):
            raise _exc_with_message(HTTPBadRequest,
                                    "Invalid distribution file.")

        # Check that if it's a binary wheel, it's on a supported platform
        if filename.endswith(".whl"):
            wheel_info = _wheel_file_re.match(filename)
            plats = wheel_info.group("plat").split(".")
            for plat in plats:
                if not _valid_platform_tag(plat):
                    raise _exc_with_message(
                        HTTPBadRequest,
                        "Binary wheel '{filename}' has an unsupported "
                        "platform tag '{plat}'.".format(filename=filename,
                                                        plat=plat),
                    )

        # Also buffer the entire signature file to disk.
        if "gpg_signature" in request.POST:
            has_signature = True
            with open(os.path.join(tmpdir, filename + ".asc"), "wb") as fp:
                signature_size = 0
                for chunk in iter(
                        lambda: request.POST["gpg_signature"].file.read(8096),
                        b""):
                    signature_size += len(chunk)
                    if signature_size > MAX_SIGSIZE:
                        raise _exc_with_message(HTTPBadRequest,
                                                "Signature too large.")
                    fp.write(chunk)

            # Check whether signature is ASCII armored
            with open(os.path.join(tmpdir, filename + ".asc"), "rb") as fp:
                if not fp.read().startswith(b"-----BEGIN PGP SIGNATURE-----"):
                    raise _exc_with_message(
                        HTTPBadRequest, "PGP signature isn't ASCII armored.")
        else:
            has_signature = False

        # TODO: This should be handled by some sort of database trigger or a
        #       SQLAlchemy hook or the like instead of doing it inline in this
        #       view.
        request.db.add(Filename(filename=filename))

        # Store the information about the file in the database.
        file_ = File(
            release=release,
            filename=filename,
            python_version=form.pyversion.data,
            packagetype=form.filetype.data,
            comment_text=form.comment.data,
            size=file_size,
            has_signature=bool(has_signature),
            md5_digest=file_hashes["md5"],
            sha256_digest=file_hashes["sha256"],
            blake2_256_digest=file_hashes["blake2_256"],
            # Figure out what our filepath is going to be, we're going to use a
            # directory structure based on the hash of the file contents. This
            # will ensure that the contents of the file cannot change without
            # it also changing the path that the file is saved too.
            path="/".join([
                file_hashes[PATH_HASHER][:2],
                file_hashes[PATH_HASHER][2:4],
                file_hashes[PATH_HASHER][4:],
                filename,
            ]),
            uploaded_via=request.user_agent,
        )
        request.db.add(file_)

        # TODO: This should be handled by some sort of database trigger or a
        #       SQLAlchemy hook or the like instead of doing it inline in this
        #       view.
        request.db.add(
            JournalEntry(
                name=release.project.name,
                version=release.version,
                action="add {python_version} file {filename}".format(
                    python_version=file_.python_version,
                    filename=file_.filename),
                submitted_by=request.user,
                submitted_from=request.remote_addr,
            ))

        # TODO: We need a better answer about how to make this transactional so
        #       this won't take affect until after a commit has happened, for
        #       now we'll just ignore it and save it before the transaction is
        #       committed.
        storage = request.find_service(IFileStorage)
        storage.store(
            file_.path,
            os.path.join(tmpdir, filename),
            meta={
                "project": file_.release.project.normalized_name,
                "version": file_.release.version,
                "package-type": file_.packagetype,
                "python-version": file_.python_version,
            },
        )
        if has_signature:
            storage.store(
                file_.pgp_path,
                os.path.join(tmpdir, filename + ".asc"),
                meta={
                    "project": file_.release.project.normalized_name,
                    "version": file_.release.version,
                    "package-type": file_.packagetype,
                    "python-version": file_.python_version,
                },
            )

    # Log a successful upload
    metrics.increment("warehouse.upload.ok",
                      tags=[f"filetype:{form.filetype.data}"])

    return Response()
Example #57
0
 def __eq__(self, other):
     return hmac.compare_digest(bytes(self), bytes(other))
Example #58
0
 def finalize_with_tag(self, tag):
     self.finalize()
     if not hmac.compare_digest(tag, self.__tag):
         raise bkx.InvalidTag  # pragma: no cover
Example #59
0
def constant_time_compare(val1, val2):
    """Return True if the two strings are equal, False otherwise."""
    return hmac.compare_digest(force_bytes(val1), force_bytes(val2))
                    stats['Bytes_sent'])
            sock.sendto(fechaFinTransmision.encode(), addr)
            sock.sendto(paquetes_recibidos.encode(), addr)
            sock.sendto(bytes_recibidos.encode(), addr)
            print('Fecha fin transmision archivo: ', fechaFinTransmision)
            f.close()
            print('Comando recibido: ', repr(END_TRANSMISSION))
            break
    digestG = digestGenerado.hexdigest().encode()
    print(digestG)
    #Se recibe el digest del servidor
    digestRecibido, addr = sock.recvfrom(buffer)
    print(digestRecibido)
    #Si el digest generado y el digest recibido no coinciden, se envia un mensaje de error.
    #Se finaliza la comunicacion con el servidor.
    if not compare_digest(digestG, digestRecibido):
        sock.sendto(ERR, addr)
        print('Comando enviado: ', repr(ERR))
        print(
            'La integridad del archivo no pudo ser verificada. Finalizando conexion.'
        )
        sock.close()
        exit()

    sock.sendto(OK, addr)

    print('La integridad del archivo pudo ser verificada correctamente.')
    print('Comando enviado: ', repr(OK))

    fin, _ = sock.recvfrom(buffer)
    if (repr(fin) != repr(FIN)):