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 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
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'])
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
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