Beispiel #1
0
    def header(self, artifacts, options=None):
        """Generate a Server-Authorization header for a given response.

        :param artifacts: A dict received from authenticate(). Contains the
                          following keys 'mac', 'hash', and 'ext'.

        :param options:
            A dict with the following structure:

            - ext: 'application-specific'
                Application specific data sent via the ext attribute.

            - payload: '{"some":"payload"}',
                UTF-8 encoded string for body hash generation (ignored if hash
                provided).

            - contentType: 'application/json',
                Payload content-type (ignored if hash provided)

            - hash: 'U4MKKSmiVxk37JCCrAVIjV='
                Pre-calculated payload hash
        }
        """
        if options is None:
            options = {}

        if not artifacts or False == isinstance(artifacts, dict) or False == isinstance(options, dict):
            return ""

        h_artifacts = copy.copy(artifacts)
        del h_artifacts["mac"]

        h_artifacts["hash"] = options.get("hash", None)

        if "ext" in options:
            h_artifacts["ext"] = options["ext"]

        credentials = self.credentials_fn(h_artifacts["id"])
        if not credentials or "key" not in credentials or "algorithm" not in credentials:
            return ""

        if "hash" not in h_artifacts or h_artifacts["hash"] is None or len(h_artifacts["hash"]) == 0:
            if "payload" in options:
                h_artifacts["hash"] = hcrypto.calculate_payload_hash(
                    options["payload"], credentials["algorithm"], options["contentType"]
                )

        mac = hcrypto.calculate_mac("response", credentials, h_artifacts)

        header = 'Hawk mac="' + mac + '"'
        if "hash" in h_artifacts:
            header += ', hash="' + h_artifacts["hash"] + '"'

        if "ext" in h_artifacts and h_artifacts["ext"] is not None and len(h_artifacts["ext"]) > 0:

            h_ext = util.check_header_attribute(h_artifacts["ext"]).replace("\\", "\\\\").replace("\n", "\\n")

            header += ', ext="' + h_ext + '"'

        return header
Beispiel #2
0
    def header(self, artifacts, options=None):
        """Generate a Server-Authorization header for a given response.

    credentials: {},                                        // Object received from authenticate()
    artifacts: {}                                           // Object received from authenticate(); 'mac', 'hash', and 'ext' - ignored
    options: {
        ext: 'application-specific',                        // Application specific data sent via the ext attribute
        payload: '{"some":"payload"}',                      // UTF-8 encoded string for body hash generation (ignored if hash provided)
        contentType: 'application/json',                    // Payload content-type (ignored if hash provided)
        hash: 'U4MKKSmiVxk37JCCrAVIjV='                     // Pre-calculated payload hash
    }
        """
        if options is None:
            options = {}

        if not artifacts or False == isinstance(artifacts, dict) or \
                False == isinstance(options, dict):
            return ''

        h_artifacts = copy.copy(artifacts)
        del h_artifacts['mac']

        h_artifacts['hash'] = options.get('hash', None)

        if 'ext' in options:
            h_artifacts['ext'] = options['ext']

        credentials = self.credentials_fn(h_artifacts['id'])
        if not credentials or 'key' not in credentials or \
                'algorithm' not in credentials:
            return ''

        if 'hash' not in h_artifacts or h_artifacts['hash'] is None or \
                len(h_artifacts['hash']) == 0:
            if 'payload' in options:
                h_artifacts['hash'] = hcrypto.calculate_payload_hash(
                    options['payload'], credentials['algorithm'],
                    options['contentType'])

        mac = hcrypto.calculate_mac('response', credentials, h_artifacts)

        header = 'Hawk mac="' + mac + '"'
        if 'hash' in h_artifacts:
            header += ', hash="' + h_artifacts['hash'] + '"'

        if 'ext' in h_artifacts and h_artifacts['ext'] is not None and \
                len(h_artifacts['ext']) > 0:

            h_ext = util.check_header_attribute(
                h_artifacts['ext']).replace('\\', '\\\\').replace('\n', '\\n')

            header += ', ext="' + h_ext + '"'

        return header
    def authenticate(self, options):
        """
        options can have the following
        * checkNonceFn - A callback to validate if a given nonce is valid
        * timestampSkewSec - Allows for clock skew in seconds. Defaults to 60.
        * localtimeOffsetMsec - Offset for client time. Defaults to 0.
        * options.payload - Required

        """
        now = math.floor(time.time())

        self._check_options(options)

        attributes = util.parse_authorization_header(
            self.req['headers']['authorization'])

        artifacts = self._prepare_artifacts(attributes)

        log.debug('artifacts=%s' % pprint.pformat(artifacts))

        credentials = self.credentials_fn(attributes['id'])
        mac = self._calculate_mac(credentials, artifacts)

        if not util.compare(mac, attributes['mac']):
            log.info("Ours [" + mac + "] Theirs [" + attributes['mac'] + "]")
            raise BadMac

        if 'payload' in options:
            if 'hash' not in attributes:
                log.info("Missing required payload hash")
                raise BadRequest
            p_hash = hcrypto.calculate_payload_hash(options['payload'],
                                                    credentials['algorithm'],
                                                    self.req['contentType'])
            log.debug('payload=%s' % options['payload'])
            log.debug('algorithm=%s, contentType=%s' %
                      (credentials['algorithm'], self.req['contentType']))
            if not util.compare(p_hash, attributes['hash']):
                log.info("Bad payload hash")
                raise BadRequest

        if 'check_nonce_fn' in options:
            if not options['check_nonce_fn'](attributes['nonce'],
                                             attributes['ts']):
                raise BadRequest

        skew = int(options['timestampSkewSec'])
        if math.fabs(int(attributes['ts']) - now) > skew:
            log.info("Expired request")
            raise BadRequest

        return artifacts
Beispiel #4
0
    def authenticate(self, options):
        """
        options can have the following
        * checkNonceFn - A callback to validate if a given nonce is valid
        * timestampSkewSec - Allows for clock skew in seconds. Defaults to 60.
        * localtimeOffsetMsec - Offset for client time. Defaults to 0.
        * options.payload - Required

        """
        now = math.floor(time.time())

        self._check_options(options)

        attributes = util.parse_authorization_header(
            self.req['headers']['authorization'])

        artifacts = self._prepare_artifacts(attributes)

        log.debug('artifacts=%s' % pprint.pformat(artifacts))

        credentials = self.credentials_fn(attributes['id'])
        mac = self._calculate_mac(credentials, artifacts)

        if not util.compare(mac, attributes['mac']):
            log.info("Ours [" + mac + "] Theirs [" + attributes['mac'] + "]")
            raise BadMac

        if 'payload' in options:
            if 'hash' not in attributes:
                log.info("Missing required payload hash")
                raise BadRequest
            p_hash = hcrypto.calculate_payload_hash(options['payload'],
                                                    credentials['algorithm'],
                                                    self.req['contentType'])
            log.debug('payload=%s' % options['payload'])
            log.debug('algorithm=%s, contentType=%s'
                      % (credentials['algorithm'], self.req['contentType']))
            if not util.compare(p_hash, attributes['hash']):
                log.info("Bad payload hash")
                raise BadRequest

        if 'check_nonce_fn' in options:
            if not options['check_nonce_fn'](attributes['nonce'],
                                             attributes['ts']):
                raise BadRequest

        skew = int(options['timestampSkewSec'])
        if math.fabs(int(attributes['ts']) - now) > skew:
            log.info("Expired request")
            raise BadRequest

        return artifacts
Beispiel #5
0
    def authenticate(self, req, options):
        """

        options can have the following
        * checkNonceFn - A callback to validate if a given nonce is valid
        * timestampSkewSec - Allows for clock skew in seconds. Defaults to 60.
        * localtimeOffsetMsec - Offset for client time. Defaults to 0.
        * options.payload - Required

        """
        now = math.floor(time.time())

        self._check_options(options)

        attributes = util.parse_authorization_header(
            req['headers']['authorization'])

        artifacts = self._prepare_artifacts(req, attributes)
        credentials = self.credentials_fn(attributes['id'])
        mac = self._calculate_mac(credentials, artifacts)

        # TODO prevent timing attach
        if not mac == attributes['mac']:
            print "Ours [" + mac + "] Theirs [" + attributes['mac'] + "]"
            raise BadMac

        if 'payload' in options:
            if 'hash' not in attributes:
                print "Missing required payload hash"
                raise BadRequest
            p_hash = hcrypto.calculate_payload_hash(options['payload'],
                                                    credentials['algorithm'],
                                                    req['contentType'])
            if not p_hash == attributes['hash']:
                print "Bad payload hash"
                raise BadRequest

        if 'check_nonce_fn' in options:
            if not options['check_nonce_fn'](attributes['nonce'],
                                             attributes['ts']):
                raise BadRequest

        skew = int(options['timestampSkewSec'])
        if math.fabs(int(attributes['ts'].split(".")[0]) - now) > skew:
            print "Expired request"
            raise BadTimingRequest(now)

        return artifacts
def header(url, method, options=None):
    """
    :param uri: 'http://example.com/resource?a=b'
    :param method: HTTP verb ('GET', 'POST', etc)
    :param options:

    Required Options:
    credentials (id, key, algorithm)

    Optional:
    ext:
    Application specific data (string)
    timestamp:
    A pre-calculated timestamp
    nonce:
    '2334f34f':  A pre-generated nonce
    localtimeOffsetMsec:
    Time offset to sync with server time (ignored if timestamp
    provided) (Example 400)
    payload:
    UTF-8 encoded string for body hash generation (ignored if hash
    provided) (Example '{"some":"payload"}')
    contentType:
    Payload content-type (ignored if hash provided) (Example
    'application/json')
    hash:
    Pre-calculated payload hash (Example 'U4MKKSmiVxk37JCCrAVIjV=')
    app:
    Oz application id ('24s23423f34dx')
    dlg:
    Oz delegated-by application id - '234sz34tww3sd'
    """
    result = {'field': '', 'artifacts': {}}

    if url is None or len(url) == 0:
        log.info("Bad URL skipping")
        return result

    if method is None or len(method) == 0:
        log.info("Bad method skipping")
        return result

    if not isinstance(options, dict):
        log.info("Bad options skipping")
        return result

    if 'credentials' not in options:
        log.info("Bad credentials skipping")
        return result

    cred = options['credentials']
    if 'id' not in cred or 'key' not in cred or 'algorithm' not in cred:
        log.info("Bad credentail elements skipping")
        return result

    timestamp = math.floor(time.time())
    if 'timestamp' in options:
        offset = 0
        if 'localtimeOffsetMsec' in options:
            offset = int(options['localtimeOffsetMsec'])
        timestamp = math.floor(options['timestamp'] + offset)

    if 'nonce' not in options:
        options['nonce'] = hcrypto.random_string(6)

    url_parts = util.parse_normalized_url(url)

    # TODO use None or '' for these optional artifacts?
    if 'hash' not in options:
        options['hash'] = None
    if 'ext' not in options:
        options['ext'] = None
    if 'app' not in options:
        options['app'] = None
    if 'dlg' not in options:
        options['dlg'] = None

    resource = url_parts['resource']

    log.debug('parsed URL parts: %s' % pprint.pformat(url_parts))

    artifacts = {
        'ts': int(timestamp),
        'nonce': options['nonce'],
        'method': method,
        'resource': resource,
        'host': url_parts['hostname'],
        'port': url_parts['port'],
        'hash': options['hash'],
        'ext': options['ext'],
        'app': options['app'],
        'dlg': options['dlg']
    }

    result['artifacts'] = artifacts

    if artifacts['hash'] is None and 'payload' in options:
        if 'contentType' not in options:
            options['contentType'] = 'text/plain'
        log.debug('about to hash payload: %s' % options['payload'])
        log.debug('algorithm=%s, contentType=%s' %
                  (cred['algorithm'], options['contentType']))
        artifacts['hash'] = hcrypto.calculate_payload_hash(
            options['payload'], cred['algorithm'], options['contentType'])

    log.debug('artifacts=%s' % pprint.pformat(artifacts))

    mac = hcrypto.calculate_mac('header', cred, artifacts)

    _header = ''.join([
        'Hawk id="',
        cred['id'],
        '"',
        ', ts="',
        str(artifacts['ts']),
        '"',
        ', nonce="',
        artifacts['nonce'],
        '"',
    ])

    if len(artifacts['hash']) > 0:
        _header += ', hash="' + artifacts['hash'] + '"'

    if artifacts['ext'] is not None and len(artifacts['ext']) > 0:
        util.check_header_attribute(artifacts['ext'])
        h_ext = artifacts['ext'].replace('\\', '\\\\').replace('\n', '\\n')
        _header += ', ext="' + h_ext + '"'

    _header += ', mac="' + mac + '"'

    if artifacts['app'] is not None:
        _header += ', app="' + artifacts['app'] + '"'
        if artifacts['dlg'] is not None:
            _header += ', dlg="' + artifacts['dlg'] + '"'

    result['field'] = _header

    return result
def authenticate(response, credentials, artifacts, options=None):
    """Validate server response.

    :param response: dictionary with server response
    :param artifacts:  object recieved from header().artifacts
    :param options: {
    payload:    optional payload received
    required:   specifies if a Server-Authorization header is required.
    Defaults to 'false'
    }
    """
    if not isinstance(response, dict) or 'headers' not in response:
        return False

    if 'content-type' not in response['headers']:
        log.warn("response lacked content-type")
        response['headers']['content-type'] = 'text/plain'

    if options is None:
        options = {}

    if 'required' not in options:
        options['required'] = False

    if 'www-authenticate' in response['headers']:
        www_auth_attrs = util.parse_authorization_header(
            response['headers']['www-authenticate'], ['ts', 'tsm', 'error'])

        if 'ts' in www_auth_attrs:
            ts_mac = hcrypto.calculate_ts_mac(www_auth_attrs['ts'],
                                              credentials)
            if not util.compare(ts_mac, www_auth_attrs['ts']):
                log.info(ts_mac + " didn't match " + www_auth_attrs['ts'])
                return False

    if 'server-authorization' not in response['headers'] and \
            False == options['required']:
        return True

    if 'server-authorization' not in response['headers']:
        log.info("Unable to verify, no server-authorization header")
        return False

    s_auth_attrs = util.parse_authorization_header(
        response['headers']['server-authorization'], ['mac', 'ext', 'hash'])
    if 'ext' in s_auth_attrs:
        artifacts['ext'] = s_auth_attrs['ext']
    else:
        artifacts['ext'] = ''

    artifacts['hash'] = s_auth_attrs['hash']

    mac = hcrypto.calculate_mac('response', credentials, artifacts)
    if not util.compare(mac, s_auth_attrs['mac']):
        log.info("server mac mismatch " + mac + " != " + s_auth_attrs['mac'])
        return False

    if 'payload' not in options:
        return True

    if 'hash' not in s_auth_attrs:
        return False

    content_type = response['headers']['content-type']
    p_mac = hcrypto.calculate_payload_hash(options['payload'],
                                           credentials['algorithm'],
                                           content_type)
    if not util.compare(p_mac, s_auth_attrs['hash']):
        log.info("p_mac " + p_mac + " != " + s_auth_attrs['hash'])

    return util.compare(p_mac, s_auth_attrs['hash'])
Beispiel #8
0
def header(url, method, options=None):
    """
    :param uri: 'http://example.com/resource?a=b'
    :param method: HTTP verb ('GET', 'POST', etc)
    :param options:

    Required Options:
    credentials (id, key, algorithm)

    Optional:
    ext:
    Application specific data (string)
    timestamp:
    A pre-calculated timestamp
    nonce:
    '2334f34f':  A pre-generated nonce
    localtimeOffsetMsec:
    Time offset to sync with server time (ignored if timestamp
    provided) (Example 400)
    payload:
    UTF-8 encoded string for body hash generation (ignored if hash
    provided) (Example '{"some":"payload"}')
    contentType:
    Payload content-type (ignored if hash provided) (Example
    'application/json')
    hash:
    Pre-calculated payload hash (Example 'U4MKKSmiVxk37JCCrAVIjV=')
    app:
    Oz application id ('24s23423f34dx')
    dlg:
    Oz delegated-by application id - '234sz34tww3sd'
    """
    result = {'field': '', 'artifacts': {}}

    if url is None or len(url) == 0:
        print "Bad URL skipping"
        return result

    if method is None or len(method) == 0:
        print "Bad method skipping"
        return result

    if not isinstance(options, dict):
        print "Bad options skipping"
        return result

    if 'credentials' not in options:
        print "Bad credentials skipping"
        return result

    cred = options['credentials']
    if 'id' not in cred or 'key' not in cred or 'algorithm' not in cred:
        print "Bad credentail elements skipping"
        return result

    timestamp = math.floor(time.time())
    if 'timestamp' in options:
        offset = 0
        if 'localtimeOffsetMsec' in options:
            offset = int(options['localtimeOffsetMsec'])
        timestamp = math.floor(options['timestamp'] + offset)

    if 'nonce' not in options:
        options['nonce'] = hcrypto.random_string(6)

    url_parts = parse_normalized_url(url)

    # TODO use None or '' for these optional artifacts?
    if 'hash' not in options:
        options['hash'] = None
    if 'ext' not in options:
        options['ext'] = None
    if 'app' not in options:
        options['app'] = None
    if 'dlg' not in options:
        options['dlg'] = None

    resource = url_parts['path']
    if len(url_parts['query']) > 0:
        resource += '?' + url_parts['query']

    artifacts = {
        'ts': int(timestamp),
        'nonce': options['nonce'],
        'method': method,
        'resource': resource,
        'host': url_parts['hostname'],
        'port': url_parts['port'],
        'hash': options['hash'],
        'ext': options['ext'],
        'app': options['app'],
        'dlg': options['dlg']
    }

    result['artifacts'] = artifacts

    if artifacts['hash'] is None and 'payload' in options:
        if 'contentType' not in options:
            options['contentType'] = 'text/plain'
        artifacts['hash'] = hcrypto.calculate_payload_hash(
               options['payload'], cred['algorithm'], options['contentType'])

    mac = hcrypto.calculate_mac('header', cred, artifacts)

    _header = ''.join([
        'Hawk id="', cred['id'], '"',
        ', ts="', str(artifacts['ts']), '"',
        ', nonce="', artifacts['nonce'], '"',
    ])

    if len(artifacts['hash']) > 0:
        _header += ', hash="' + artifacts['hash'] + '"'

    if artifacts['ext'] is not None and len(artifacts['ext']) > 0:
        util.check_header_attribute(artifacts['ext'])
        h_ext = artifacts['ext'].replace('\\', '\\\\').replace('\n', '\\n')
        _header += ', ext="' + h_ext + '"'

    _header += ', mac="' + mac + '"'

    if artifacts['app'] is not None:
        _header += ', app="' + artifacts['app'] + '"'
        if artifacts['dlg'] is not None:
            _header += ', dlg="' + artifacts['dlg'] + '"'

    result['field'] = _header

    return result
Beispiel #9
0
def authenticate(response, credentials, artifacts, options=None):
    """Validate server response.

    :param response: dictionary with server response
    :param artifacts:  object recieved from header().artifacts
    :param options: {
    payload:    optional payload received
    required:   specifies if a Server-Authorization header is required.
    Defaults to 'false'
    }
    """
    if not isinstance(response, dict) or 'headers' not in response:
        return False

    if 'content-type' not in response['headers']:
        print "WARNING response lacked content-type"
        response['headers']['content-type'] = 'text/plain'

    if options is None:
        options = {}

    if 'required' not in options:
        options['required'] = False

    if 'www-authenticate' in response['headers']:
        www_auth_attrs = util.parse_authorization_header(
            response['headers']['www-authenticate'],
            ['ts', 'tsm', 'error'])

        if 'ts' in www_auth_attrs:
            ts_mac = hcrypto.calculate_ts_mac(www_auth_attrs['ts'],
                                                  credentials)
            if not util.compare(ts_mac, www_auth_attrs['ts']):
                print ts_mac + " didn't match " + www_auth_attrs['ts']
                return False

    if 'server-authorization' not in response['headers'] and \
            False == options['required']:
        return True

    if 'server-authorization' not in response['headers']:
        print "Unable to verify, no server-authorization header"
        return False

    s_auth_attrs = util.parse_authorization_header(
        response['headers']['server-authorization'],
                ['mac', 'ext', 'hash'])
    if 'ext' in s_auth_attrs:
        artifacts['ext'] = s_auth_attrs['ext']
    else:
        artifacts['ext'] = ''

    artifacts['hash'] = s_auth_attrs['hash']

    mac = hcrypto.calculate_mac('response', credentials, artifacts)
    if not util.compare(mac, s_auth_attrs['mac']):
        print "server mac mismatch " + mac + " != " + s_auth_attrs['mac']
        return False

    if 'payload' in options:
        return True

    if 'hash' not in s_auth_attrs:
        return False

    content_type = response['headers']['content-type']
    p_mac = hcrypto.calculate_payload_hash(options['payload'],
                                           credentials['algorithm'],
                                           content_type)
    if not util.compare(p_mac, s_auth_attrs['hash']):
        print "p_mac " + p_mac + " != " + s_auth_attrs['hash']

    return util.compare(p_mac, s_auth_attrs['hash'])
    def header(self, artifacts, options=None):
        """Generate a Server-Authorization header for a given response.

        :param artifacts: A dict received from authenticate(). Contains the
                          following keys 'mac', 'hash', and 'ext'.

        :param options:
            A dict with the following structure:

            - ext: 'application-specific'
                Application specific data sent via the ext attribute.

            - payload: '{"some":"payload"}',
                UTF-8 encoded string for body hash generation (ignored if hash
                provided).

            - contentType: 'application/json',
                Payload content-type (ignored if hash provided)

            - hash: 'U4MKKSmiVxk37JCCrAVIjV='
                Pre-calculated payload hash
        }
        """
        if options is None:
            options = {}

        if not artifacts or False == isinstance(artifacts, dict) or \
                False == isinstance(options, dict):
            return ''

        h_artifacts = copy.copy(artifacts)
        del h_artifacts['mac']

        h_artifacts['hash'] = options.get('hash', None)

        if 'ext' in options:
            h_artifacts['ext'] = options['ext']

        credentials = self.credentials_fn(h_artifacts['id'])
        if not credentials or 'key' not in credentials or \
                'algorithm' not in credentials:
            return ''

        if 'hash' not in h_artifacts or h_artifacts['hash'] is None or \
                len(h_artifacts['hash']) == 0:
            if 'payload' in options:
                h_artifacts['hash'] = hcrypto.calculate_payload_hash(
                    options['payload'], credentials['algorithm'],
                    options['contentType'])

        mac = hcrypto.calculate_mac('response', credentials, h_artifacts)

        header = 'Hawk mac="' + mac + '"'
        if 'hash' in h_artifacts:
            header += ', hash="' + h_artifacts['hash'] + '"'

        if 'ext' in h_artifacts and h_artifacts['ext'] is not None and \
                len(h_artifacts['ext']) > 0:

            h_ext = util.check_header_attribute(h_artifacts['ext']).replace(
                '\\', '\\\\').replace('\n', '\\n')

            header += ', ext="' + h_ext + '"'

        return header