def valid_signature(payload, signature): """Checks if `signature` matches `payload`. `Signature` (at least as of version 1) be of the form: {global_version}:{platform}:{version}:{signature} where: * global_version (currently hard-coded to be "1") can be used to change this header's underlying schema later if needs be. As such, can be treated as a protocol version. * platform is the client platform type (generally "ios" or "android") * version is the client's token version (can be updated and incremented per app build as needs be. * signature is the hmac of the request's POST body with the token derived from the above three parameters via `get_secret_token` """ result = Storage( global_version=-1, platform=None, version=-1, mac=None, valid=False, epoch=None, error=ERRORS.UNKNOWN, ) sig_match = SIG_HEADER_RE.match(signature or "") if not sig_match: result.error = ERRORS.INVALID_FORMAT return result sig_header_dict = sig_match.groupdict() # we're matching \d so this shouldn't throw a TypeError result.global_version = int(sig_header_dict['global_version']) # incrementing this value is drastic. We can't validate a token protocol # we don't understand. if result.global_version > GLOBAL_TOKEN_VERSION: result.error = ERRORS.UNKOWN_GLOBAL_VERSION return result # currently there's only one version, but here's where we'll eventually # patch in more. sig_match = SIG_CONTENT_V1_RE.match(sig_header_dict['payload']) if not sig_match: result.error = ERRORS.UNPARSEABLE return result result.update(sig_match.groupdict()) result.version = int(result.version) result.epoch = int(result.epoch) # verify that the token provided hasn't been invalidated if is_invalid_token(result.platform, result.version): result.error = ERRORS.INVALIDATED_TOKEN return result if not valid_epoch(result.platform, result.epoch): result.error = ERRORS.EXPIRED_TOKEN return result # get the expected secret used to verify this request. secret_token = get_secret_token( result.platform, result.version, global_version=result.global_version, ) result.valid = constant_time_compare( result.mac, versioned_hmac( secret_token, epoch_wrap(result.epoch, payload), result.global_version ), ) if result.valid: result.error = None else: result.error = ERRORS.SIGNATURE_MISMATCH return result
def valid_signature(payload, signature): """Checks if `signature` matches `payload`. `Signature` (at least as of version 1) be of the form: {global_version}:{platform}:{version}:{signature} where: * global_version (currently hard-coded to be "1") can be used to change this header's underlying schema later if needs be. As such, can be treated as a protocol version. * platform is the client platform type (generally "ios" or "android") * version is the client's token version (can be updated and incremented per app build as needs be. * signature is the hmac of the request's POST body with the token derived from the above three parameters via `get_secret_token` """ result = Storage( global_version=-1, platform=None, version=-1, mac=None, valid=False, epoch=None, error=ERRORS.UNKNOWN, ) sig_match = SIG_HEADER_RE.match(signature or "") if not sig_match: result.error = ERRORS.INVALID_FORMAT return result sig_header_dict = sig_match.groupdict() # we're matching \d so this shouldn't throw a TypeError result.global_version = int(sig_header_dict['global_version']) # incrementing this value is drastic. We can't validate a token protocol # we don't understand. if result.global_version > GLOBAL_TOKEN_VERSION: result.error = ERRORS.UNKOWN_GLOBAL_VERSION return result # currently there's only one version, but here's where we'll eventually # patch in more. sig_match = SIG_CONTENT_V1_RE.match(sig_header_dict['payload']) if not sig_match: result.error = ERRORS.UNPARSEABLE return result result.update(sig_match.groupdict()) result.version = int(result.version) result.epoch = int(result.epoch) # verify that the token provided hasn't been invalidated if is_invalid_token(result.platform, result.version): result.error = ERRORS.INVALIDATED_TOKEN return result if not valid_epoch(result.platform, result.epoch): result.error = ERRORS.EXPIRED_TOKEN return result # get the expected secret used to verify this request. secret_token = get_secret_token( result.platform, result.version, global_version=result.global_version, ) result.valid = constant_time_compare( result.mac, versioned_hmac(secret_token, epoch_wrap(result.epoch, payload), result.global_version), ) if result.valid: result.error = None else: result.error = ERRORS.SIGNATURE_MISMATCH return result