Exemple #1
0
    def _calculate_mac(self, credentials, artifacts):
        """Checks inputs and calculates MAC."""
        if 'key' not in credentials or 'algorithm' not in credentials:
            raise MissingCredentials
        mac = hcrypto.calculate_mac('header', credentials, artifacts)

        return mac
Exemple #2
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
    def _calculate_mac(self, credentials, artifacts):
        """Checks inputs and calculates MAC."""
        if 'key' not in credentials or 'algorithm' not in credentials:
            raise MissingCredentials

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

        return mac
Exemple #4
0
    def _calculate_mac(self, credentials, artifacts):
        """Checks inputs and calculates MAC."""
        if "key" not in credentials or "algorithm" not in credentials:
            raise MissingCredentials

        mac = hcrypto.calculate_mac("header", credentials, artifacts)

        return mac
Exemple #5
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
Exemple #6
0
    def authenticate_bewit(self, req, credentials, options):
        """Authenticate bewit one time requests.

        Compatibility Note: HAWK exposes this as hawk.uri.authenticate

        req is a dict with the keys:
        url
        method


        Optional options: 'hostHeaderName', 'localtimeOffsetMsec'
        
        """
        if not valid_bewit_args(req, options):            
            return False
        now = time.time() + int(options['localtime_offset_msec'])

        url = urlparse(req['url'])
        qs = parse_qs(url.query)

        if not 'bewit' in qs or len(qs['bewit']) != 1 or \
                len(qs['bewit'][0]) == 0:
            print "No bewit query string parameter"
            return False

        bewit = hcrypto.explode_bewit(qs['bewit'][0])

        original_url = normalize_url_without_bewit(req['url'], qs['bewit'][0])

        if bewit['exp'] < now:
            raise BewitExpired

        options['ts'] = bewit['exp']

        artifacts = {
            'ts': bewit['exp'],
            'nonce': '',
            'method': 'GET',
            'resource': original_url,
            'host': req['host'],
            'port': req['port'],
            'ext': bewit['ext']
        }

        mac = hcrypto.calculate_mac('bewit', credentials, artifacts, True)

        # TODO mitigate timing attack
        if mac != bewit['mac']:
            print "bewit " + mac + " didn't match " + bewit['mac']
            raise BadRequest

        return True
    def authenticate_bewit(self, options):
        """Authenticate bewit one time requests.

        Compatibility Note: HAWK exposes this as hawk.uri.authenticate

        :param options:
            A dict which may contain the 'hostHeaderName' and
            'localtimeOffsetMsec' keys.

        """
        if not valid_bewit_args(self.req, options):
            return False
        now = time.time() + int(options['localtime_offset_msec'])

        url = urlparse(self.req['url'])
        qs = parse_qs(url.query)

        if not 'bewit' in qs or len(qs['bewit']) != 1 or \
                len(qs['bewit'][0]) == 0:
            log.info("No bewit query string parameter")
            return False

        bewit = hcrypto.explode_bewit(qs['bewit'][0])

        original_url = normalize_url_without_bewit(self.req['url'],
                                                   qs['bewit'][0])

        if bewit['exp'] < now:
            raise BewitExpired

        options['ts'] = bewit['exp']

        artifacts = {
            'ts': bewit['exp'],
            'nonce': '',
            'method': 'GET',
            'resource': original_url,
            'host': self.req['host'],
            'port': self.req['port'],
            'ext': bewit['ext']
        }

        credentials = self.credentials_fn(bewit['id'])
        mac = hcrypto.calculate_mac('bewit', credentials, artifacts, True)

        if not util.compare(mac, bewit['mac']):
            log.info("bewit " + mac + " didn't match " + bewit['mac'])
            raise BadRequest

        return True
Exemple #8
0
    def authenticate_bewit(self, options):
        """Authenticate bewit one time requests.

        Compatibility Note: HAWK exposes this as hawk.uri.authenticate

        :param options:
            A dict which may contain the 'hostHeaderName' and
            'localtimeOffsetMsec' keys.

        """
        if not valid_bewit_args(self.req, options):
            return False
        now = time.time() + int(options['localtime_offset_msec'])

        url = urlparse(self.req['url'])
        qs = parse_qs(url.query)

        if not 'bewit' in qs or len(qs['bewit']) != 1 or \
                len(qs['bewit'][0]) == 0:
            log.info("No bewit query string parameter")
            return False

        bewit = hcrypto.explode_bewit(qs['bewit'][0])

        original_url = normalize_url_without_bewit(self.req['url'],
                                                   qs['bewit'][0])

        if bewit['exp'] < now:
            raise BewitExpired

        options['ts'] = bewit['exp']

        artifacts = {
            'ts': bewit['exp'],
            'nonce': '',
            'method': 'GET',
            'resource': original_url,
            'host': self.req['host'],
            'port': self.req['port'],
            'ext': bewit['ext']
        }

        credentials = self.credentials_fn(bewit['id'])
        mac = hcrypto.calculate_mac('bewit', credentials, artifacts, True)

        if not util.compare(mac, bewit['mac']):
            log.info("bewit " + mac + " didn't match " + bewit['mac'])
            raise BadRequest

        return True
Exemple #9
0
    def authenticate_bewit(self, options):
        """Authenticate bewit one time requests.

        Compatibility Note: HAWK exposes this as hawk.uri.authenticate

        :param options:
            A dict which may contain the 'hostHeaderName' and
            'localtimeOffsetMsec' keys.

        """
        if not valid_bewit_args(self.req, options):
            return False
        now = time.time() + int(options["localtime_offset_msec"])

        url = urlparse(self.req["url"])
        qs = parse_qs(url.query)

        if not "bewit" in qs or len(qs["bewit"]) != 1 or len(qs["bewit"][0]) == 0:
            log.info("No bewit query string parameter")
            return False

        bewit = hcrypto.explode_bewit(qs["bewit"][0])

        original_url = normalize_url_without_bewit(self.req["url"], qs["bewit"][0])

        if bewit["exp"] < now:
            raise BewitExpired

        options["ts"] = bewit["exp"]

        artifacts = {
            "ts": bewit["exp"],
            "nonce": "",
            "method": "GET",
            "resource": original_url,
            "host": self.req["host"],
            "port": self.req["port"],
            "ext": bewit["ext"],
        }

        credentials = self.credentials_fn(bewit["id"])
        mac = hcrypto.calculate_mac("bewit", credentials, artifacts, True)

        if not util.compare(mac, bewit["mac"]):
            log.info("bewit " + mac + " didn't match " + bewit["mac"])
            raise BadRequest

        return True
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'])
Exemple #12
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
Exemple #13
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