Send a sign in request to the master, sets the key information and
        returns a dict containing the master publish interface to bind to
        and the decrypted aes key for transport decryption.

        :param int timeout: Number of seconds to wait before timing out the sign-in request
        :param bool safe: If True, do not raise an exception on timeout. Retry instead.
        :param int tries: The number of times to try to authenticate before giving up.

        :raises SaltReqTimeoutError: If the sign-in request has timed out and :param safe: is not set

        :return: Return a string on failure indicating the reason for failure. On success, return a dictionary
        with the publication port and the shared AES key.

def sign_in(self, timeout=60, safe=True, tries=1, channel=None):
        '''
        Send a sign in request to the master, sets the key information and
        returns a dict containing the master publish interface to bind to
        and the decrypted aes key for transport decryption.

        :param int timeout: Number of seconds to wait before timing out the sign-in request
        :param bool safe: If True, do not raise an exception on timeout. Retry instead.
        :param int tries: The number of times to try to authenticate before giving up.

        :raises SaltReqTimeoutError: If the sign-in request has timed out and :param safe: is not set

        :return: Return a string on failure indicating the reason for failure. On success, return a dictionary
        with the publication port and the shared AES key.

        '''
        auth = {}

        auth_timeout = self.opts.get('auth_timeout', None)
        if auth_timeout is not None:
            timeout = auth_timeout
        auth_safemode = self.opts.get('auth_safemode', None)
        if auth_safemode is not None:
            safe = auth_safemode
        auth_tries = self.opts.get('auth_tries', None)
        if auth_tries is not None:
            tries = auth_tries

        m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub)

        auth['master_uri'] = self.opts['master_uri']

        close_channel = False
        if not channel:
            close_channel = True
            channel = salt.transport.client.AsyncReqChannel.factory(self.opts,
                                                                    crypt='clear',
                                                                    io_loop=self.io_loop)

        sign_in_payload = self.minion_sign_in_payload()
        try:
            payload = yield channel.send(
                sign_in_payload,
                tries=tries,
                timeout=timeout
            )
        except SaltReqTimeoutError as e:
            if safe:
                log.warning('SaltReqTimeoutError: %s', e)
                raise tornado.gen.Return('retry')
            if self.opts.get('detect_mode') is True:
                raise tornado.gen.Return('retry')
            else:
                raise SaltClientError('Attempt to authenticate with the salt master failed with timeout error')
        finally:
            if close_channel:
                channel.close()

        if not isinstance(payload, dict):
            log.error('Sign-in attempt failed: %s', payload)
            raise tornado.gen.Return(False)
        if 'load' in payload:
            if 'ret' in payload['load']:
                if not payload['load']['ret']:
                    if self.opts['rejected_retry']:
                        log.error(
                            'The Salt Master has rejected this minion\'s public '
                            'key.\nTo repair this issue, delete the public key '
                            'for this minion on the Salt Master.\nThe Salt '
                            'Minion will attempt to to re-authenicate.'
                        )
                        raise tornado.gen.Return('retry')
                    else:
                        log.critical(
                            'The Salt Master has rejected this minion\'s public '
                            'key!\nTo repair this issue, delete the public key '
                            'for this minion on the Salt Master and restart this '
                            'minion.\nOr restart the Salt Master in open mode to '
                            'clean out the keys. The Salt Minion will now exit.'
                        )
                        # Add a random sleep here for systems that are using a
                        # a service manager to immediately restart the service
                        # to avoid overloading the system
                        time.sleep(random.randint(10, 20))
                        sys.exit(salt.defaults.exitcodes.EX_NOPERM)
                # has the master returned that its maxed out with minions?
                elif payload['load']['ret'] == 'full':
                    raise tornado.gen.Return('full')
                else:
                    log.error(
                        'The Salt Master has cached the public key for this '
                        'node, this salt minion will wait for %s seconds '
                        'before attempting to re-authenticate',
                        self.opts['acceptance_wait_time']
                    )
                    raise tornado.gen.Return('retry')
        auth['aes'] = self.verify_master(payload, master_pub='token' in sign_in_payload)
        if not auth['aes']:
            log.critical(
                'The Salt Master server\'s public key did not authenticate!\n'
                'The master may need to be updated if it is a version of Salt '
                'lower than %s, or\n'
                'If you are confident that you are connecting to a valid Salt '
                'Master, then remove the master public key and restart the '
                'Salt Minion.\nThe master public key can be found '
                'at:\n%s', salt.version.__version__, m_pub_fn
            )
            raise SaltClientError('Invalid master key')
        if self.opts.get('syndic_master', False):  # Is syndic
            syndic_finger = self.opts.get('syndic_finger', self.opts.get('master_finger', False))
            if syndic_finger:
                if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != syndic_finger:
                    self._finger_fail(syndic_finger, m_pub_fn)
        else:
            if self.opts.get('master_finger', False):
                if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != self.opts['master_finger']:
                    self._finger_fail(self.opts['master_finger'], m_pub_fn)
        auth['publish_port'] = payload['publish_port']
        raise tornado.gen.Return(auth)