def _send_request(self, path, data): """ Sends request to the FURS Server and decodes response. :param path: (string) Server path :param data: (dict) Data to be sent :return: (dict) Received response :raises: ConnectionTimedOutException: If connection timed out ConnectionException: If FURS responded with status code different than 200 FURSException: If server responded with error """ try: response = self.connector.post(path=path, json=data) if response.status_code == codes.ok: # TODO - we should verify server signature! server_response = jws.get_unverified_claims(response.json()['token']) return self._check_for_errors(server_response) else: raise ConnectionException(code=response.status_code, message=response.text) except Timeout as e: raise ConnectionTimedOutException(e)
def get_unverified_claims(token): """Returns the decoded claims without verification of any kind. Args: token (str): A signed JWT to decode the headers from. Returns: dict: The dict representation of the token claims. Raises: JWTError: If there is an exception decoding the token. """ try: claims = jws.get_unverified_claims(token) except: raise JWTError('Error decoding token claims.') try: claims = json.loads(claims.decode('utf-8')) except ValueError as e: raise JWTError('Invalid claims string: %s' % e) if not isinstance(claims, Mapping): raise JWTError('Invalid claims string: must be a json object') return claims
def get_unverified_claims(token): """Returns the decoded claims without verification of any kind. Args: token (str): A signed JWT to decode the headers from. Returns: dict: The dict representation of the token claims. Raises: JWTError: If there is an exception decoding the token. """ try: claims = jws.get_unverified_claims(token) except Exception: raise JWTError('Error decoding token claims.') try: claims = json.loads(claims.decode('utf-8')) except ValueError as e: raise JWTError('Invalid claims string: %s' % e) if not isinstance(claims, Mapping): raise JWTError('Invalid claims string: must be a json object') return claims
def _decode_vc(jws_raw, key_resolver): # before we can verify the vc, we first need to resolve the key # the key ID is stored in the header # Per the health cards IG, ## "Issuers SHALL publish keys as JSON Web Key Sets (see RFC7517), available at <<iss value from Signed JWT>> + .well-known/jwks.json" # therefore, we need decode the claims to get the iss value in order to resolve the key # The claims are compressed via Deflate, so decompress the data # then, extract the iss claim to get access to the base URL, use that to resolve key with id = kid # then, verify the jws unverified_headers = jws.get_unverified_headers(jws_raw) # we expect data to be zipped, so deflate the data if unverified_headers.get('zip') == 'DEF': unverfied_claims_zip = jws.get_unverified_claims(jws_raw) raw_data = inflate(unverfied_claims_zip) data = json.loads(raw_data) else: raise Exception('Expecting payload to be compressed') iss = data['iss'] kid = unverified_headers['kid'] key = key_resolver(iss, kid, 'ES256') verified_jws = jws.verify(jws_raw, key, algorithms='ES256') payload = json.loads(inflate(verified_jws)) return payload
def _send_request(self, path, data): """ Sends request to the FURS Server and decodes response. :param path: (string) Server path :param data: (dict) Data to be sent :return: (dict) Received response :raises: ConnectionTimedOutException: If connection timed out ConnectionException: If FURS responded with status code different than 200 FURSException: If server responded with error """ try: response = self.connector.post(path=path, json=data) if response.status_code == codes.ok: # TODO - we should verify server signature! server_response = json.loads( jws.get_unverified_claims(response.json()['token'])) return self._check_for_errors(server_response) else: raise ConnectionException(code=response.status_code, message=response.text) except Timeout as e: raise ConnectionTimedOutException(e)
def _extract_token_body() -> dict: if getattr(flask.g, "token_body", None): return flask.g.token_body token = _http._get_token(flask.request.headers) if not token: return {} try: return json.loads(jws.get_unverified_claims(token=token)) except exceptions.JOSEError: return {}
def validate_signatures(body): att_stmts = [] if isinstance(body, list): for s in body: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in body: att_stmts.append(body) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s, a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: sha2_key = ss[1]['sha2'] signature = att_cache.get(sha2_key) algorithm = jws.get_unverified_headers(signature).get('alg', None) x5c = jws.get_unverified_headers(signature).get('x5c', None) jws_payload = jws.get_unverified_claims(signature) body_payload = ss[0] # If x.509 was used to sign, the public key should be in the x5c header and you need to verify it # If using RS256, RS384, or RS512 some JWS libs require a real private key to create JWS - xAPI spec # only has SHOULD - need to look into. If x.509 is necessary then if no x5c header is found this should fail if x5c: verified = False try: verified = jws.verify(signature, cert_to_key(x5c[0]), algorithm) except Exception, e: att_cache.delete(sha2_key) raise BadRequest("The JWS is not valid: %s" % e.message) else: if not verified: att_cache.delete(sha2_key) raise BadRequest( "The JWS is not valid - could not verify signature" ) # Compare statements if not compare_payloads(jws_payload, body_payload): att_cache.delete(sha2_key) raise BadRequest( "The JWS is not valid - payload and body statements do not match" ) else: # Compare statements if not compare_payloads(jws_payload, body_payload): att_cache.delete(sha2_key) raise BadRequest( "The JWS is not valid - payload and body statements do not match" )
def _try_decode(self, openid_credentials: dict) -> dict: id_token = openid_credentials["id_token"] # "access_token" must only be provided if claim is provided, else "jwt.decode" throws... if b"at_hash" in jws.get_unverified_claims(id_token): access_token = openid_credentials["access_token"] else: access_token = None header = jws.get_unverified_header(id_token) verified_id_token = jwt.decode( id_token, self._jwk_pkey.key(header["kid"]), issuer=self._issuer, access_token=access_token, audience=self._client_id, ) return verified_id_token
def _decode_with_cert(token, key, hint=None): kid = key.get('kid', None) tokenkid = None pubcert = None jwtson = None tokendecode = jjws.get_unverified_claims(token) tokenkid = jjws.get_unverified_header(token).get("kid") if (tokenkid and kid) and (kid == tokenkid): try: jwtson = jjwt.decode(token, key, algorithms='RS256', options={'verify_aud': False}) return jwtson except Exception as e: print("kid in token: Cannot be introspected in JWKS %s" % e)
def get_unverified_claims(token): """Returns the decoded claims without verification of any kind. Args: token (str): A signed JWT to decode the headers from. Returns: dict: The dict representation of the token claims. Raises: JWTError: If there is an exception decoding the token. """ try: claims = jws.get_unverified_claims(token) except: raise JWTError('Error decoding token claims.') return claims
def validate_signatures(body): att_stmts = [] if isinstance(body, list): for s in body: if 'attachments' in s: att_stmts.append(s) elif 'attachments' in body: att_stmts.append(body) if att_stmts: # find if any of those statements with attachments have a signed statement signed_stmts = [(s,a) for s in att_stmts for a in s.get('attachments', None) if a['usageType'] == "http://adlnet.gov/expapi/attachments/signature"] for ss in signed_stmts: sha2_key = ss[1]['sha2'] signature = att_cache.get(sha2_key) algorithm = jws.get_unverified_headers(signature).get('alg', None) x5c = jws.get_unverified_headers(signature).get('x5c', None) jws_payload = jws.get_unverified_claims(signature) body_payload = ss[0] # If x.509 was used to sign, the public key should be in the x5c header and you need to verify it # If using RS256, RS384, or RS512 some JWS libs require a real private key to create JWS - xAPI spec # only has SHOULD - need to look into. If x.509 is necessary then if no x5c header is found this should fail if x5c: verified = False try: verified = jws.verify(signature, cert_to_key(x5c[0]), algorithm) except Exception, e: att_cache.delete(sha2_key) raise BadRequest("The JWS is not valid: %s" % e.message) else: if not verified: att_cache.delete(sha2_key) raise BadRequest("The JWS is not valid - could not verify signature") # Compare statements if not compare_payloads(jws_payload, body_payload): att_cache.delete(sha2_key) raise BadRequest("The JWS is not valid - payload and body statements do not match") else: # Compare statements if not compare_payloads(jws_payload, body_payload): att_cache.delete(sha2_key) raise BadRequest("The JWS is not valid - payload and body statements do not match")
def validate_signature(tup, part): sha2_key = tup[1][0] signature = get_part_payload(part) algorithm = jws.get_unverified_headers(signature).get('alg', None) if not algorithm: raise BadRequest( "No signing algorithm found for JWS signature") if algorithm != 'RS256' and algorithm != 'RS384' and algorithm != 'RS512': raise BadRequest( "JWS signature must be calculated with SHA-256, SHA-384 or" \ "SHA-512 algorithms") x5c = jws.get_unverified_headers(signature).get('x5c', None) jws_payload = jws.get_unverified_claims(signature) body_payload = tup[0] # If x.509 was used to sign, the public key should be in the x5c header and you need to verify it # If using RS256, RS384, or RS512 some JWS libs require a real private key to create JWS - xAPI spec # only has SHOULD - need to look into. If x.509 is necessary then # if no x5c header is found this should fail if x5c: verified = False try: verified = jws.verify( signature, cert_to_key(x5c[0]), algorithm) except Exception as e: raise BadRequest("The JWS is not valid: %s" % e.message) else: if not verified: raise BadRequest( "The JWS is not valid - could not verify signature") # Compare statements if not compare_payloads(jws_payload, body_payload, sha2_key): raise BadRequest( "The JWS is not valid - payload and body statements do not match") else: # Compare statements if not compare_payloads(jws_payload, body_payload, sha2_key): raise BadRequest( "The JWS is not valid - payload and body statements do not match")
def validate_signature(tup, part): sha2_key = tup[1][0] signature = get_part_payload(part) algorithm = jws.get_unverified_headers(signature).get('alg', None) if not algorithm: raise BadRequest("No signing algorithm found for JWS signature") if algorithm != 'RS256' and algorithm != 'RS384' and algorithm != 'RS512': raise BadRequest( "JWS signature must be calculated with SHA-256, SHA-384 or" \ "SHA-512 algorithms") x5c = jws.get_unverified_headers(signature).get('x5c', None) jws_payload = jws.get_unverified_claims(signature) body_payload = tup[0] # If x.509 was used to sign, the public key should be in the x5c header and you need to verify it # If using RS256, RS384, or RS512 some JWS libs require a real private key to create JWS - xAPI spec # only has SHOULD - need to look into. If x.509 is necessary then # if no x5c header is found this should fail if x5c: verified = False try: verified = jws.verify(signature, cert_to_key(x5c[0]), algorithm) except Exception as e: raise BadRequest("The JWS is not valid: %s" % e.message) else: if not verified: raise BadRequest( "The JWS is not valid - could not verify signature") # Compare statements if not compare_payloads(jws_payload, body_payload, sha2_key): raise BadRequest( "The JWS is not valid - payload and body statements do not match" ) else: # Compare statements if not compare_payloads(jws_payload, body_payload, sha2_key): raise BadRequest( "The JWS is not valid - payload and body statements do not match" )
def process_jws_input(state, task_meta, **options): try: data = task_meta['data'] except KeyError: raise TaskPrerequisitesError() node_json = jws.get_unverified_claims(data).decode('utf-8') node_data = json.loads(node_json) node_id = task_meta.get('node_id', node_data.get('id')) actions = [ add_task(INTAKE_JSON, data=node_json, node_id=node_id), add_task(VERIFY_JWS, node_id=node_id, data=data, prerequisites=SIGNING_KEY_FETCHED) ] if node_id: actions.append(set_validation_subject(node_id)) return task_result( True, "Processed JWS-signed data and queued signature verification task", actions)