def test_verify_of_baked_image(self): url = 'https://example.org/beths-robotics-badge.json' png_badge = os.path.join(os.path.dirname(__file__), 'testfiles', 'public_domain_heart.png') responses.add(responses.GET, url, body=test_components['2_0_basic_assertion'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/beths-robot-badge.png') responses.add(responses.GET, 'https://w3id.org/openbadges/v2', body=test_components['openbadges_context'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/robotics-badge.json', body=test_components['2_0_basic_badgeclass'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/robotics-badge.png') responses.add(responses.GET, 'https://example.org/organization.json', body=test_components['2_0_basic_issuer'], status=200, content_type='application/ld+json') with open(png_badge, 'rb') as image: baked_image = bake(image, test_components['2_0_basic_assertion']) responses.add(responses.GET, 'https://example.org/baked', body=baked_image.read(), content_type='image/png') results = verify(baked_image) # verify gets the JSON out of the baked image, and then detect_input_type # will reach out to the assertion URL to fetch the canonical assertion (thus, # we expect this to become an URL input type for the verifier). self.assertNotEqual(results, None) self.assertEqual(results.get('input').get('value'), url) self.assertEqual(results.get('input').get('input_type'), 'url') self.assertEqual(len(results['report']['messages']), 0, "There should be no failing tasks.") # Verify that the same result occurs when passing in the baked image url. another_result = verify('https://example.org/baked') self.assertTrue(another_result['report']['valid']) self.assertEqual(another_result['report']['validationSubject'], results['report']['validationSubject'])
def test_verify_function(self): url = 'https://example.org/beths-robotics-badge.json' responses.add(responses.GET, url, body=test_components['2_0_basic_assertion'], status=200, content_type='application/ld+json') set_up_image_mock('https://example.org/beths-robot-badge.png') responses.add(responses.GET, 'https://w3id.org/openbadges/v2', body=test_components['openbadges_context'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/robotics-badge.json', body=test_components['2_0_basic_badgeclass'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/robotics-badge.png') responses.add(responses.GET, 'https://example.org/organization.json', body=test_components['2_0_basic_issuer'], status=200, content_type='application/ld+json') results = verify(url) self.assertEqual(results.get('input').get('value'), url) self.assertEqual(results.get('input').get('input_type'), 'url') self.assertEqual(len(results['report']['messages']), 0, "There should be no failing tasks.")
def test_verify_with_original_json(self): url = 'https://example.org/beths-robotics-badge.json' responses.add(responses.GET, url, body=test_components['2_0_basic_assertion'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://w3id.org/openbadges/v2', body=test_components['openbadges_context'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/robotics-badge.json', body=test_components['2_0_basic_badgeclass'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/organization.json', body=test_components['2_0_basic_issuer'], status=200, content_type='application/ld+json') result = verify(url, include_original_json=True) self.assertIn('original_json', list(result['input'].keys())) self.assertEqual(len(result['input']['original_json']), 3) self.assertIn(url, list(result['input']['original_json'].keys()))
def test_can_full_verify_with_revocation_check(self): input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} set_up_image_mock(u'https://example.org/beths-robot-badge.png') input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) set_up_image_mock(input_badgeclass['image']) revocation_list = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': 'http://example.org/revocationList', 'type': 'RevocationList', 'revokedAssertions': []} input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['revocationList'] = revocation_list['id'] input_issuer['publicKey'] = input_assertion['verification']['creator'] private_key = RSA.generate(2048) print("PKEY") print(private_key.publickey().exportKey('PEM').decode()) cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': private_key.publickey().exportKey('PEM').decode() } set_up_context_mock() for doc in [input_assertion, input_badgeclass, input_issuer, cryptographic_key_doc, revocation_list]: responses.add(responses.GET, doc['id'], json=doc, status=200) header = json.dumps({'alg': 'RS256'}) payload = json.dumps(input_assertion) encoded_separator = '.' if not sys.version[:3] < '3': encoded_separator = '.'.encode() encoded_header = b64encode(header.encode()) encoded_payload = b64encode(payload.encode()) else: encoded_header = b64encode(header) encoded_payload = b64encode(payload) signature = encoded_separator.join([ encoded_header, encoded_payload, jws.sign(header, payload, private_key, is_json=True) ]) response = verify(signature, use_cache=False) self.assertTrue(response['report']['valid'])
def test_verify_redirected_validation_subject(self): url = 'https://example.org/beths-robotics-badge.json' assertion_data = json.loads(test_components['2_0_basic_assertion']) alt_assertion_url = 'http://example.org/altbadgeurl' assertion_data['id'] = alt_assertion_url responses.add(responses.GET, url, json=assertion_data, status=200, content_type='application/ld+json') responses.add(responses.GET, alt_assertion_url, json=assertion_data, status=200, content_type='application/ld+json') set_up_image_mock('https://example.org/beths-robot-badge.png') responses.add(responses.GET, 'https://w3id.org/openbadges/v2', body=test_components['openbadges_context'], status=200, content_type='application/ld+json') responses.add(responses.GET, 'https://example.org/robotics-badge.json', body=test_components['2_0_basic_badgeclass'], status=200, content_type='application/ld+json') set_up_image_mock(u'https://example.org/robotics-badge.png') responses.add(responses.GET, 'https://example.org/organization.json', body=test_components['2_0_basic_issuer'], status=200, content_type='application/ld+json') results = verify(url) self.assertEqual(len(results['report']['messages']), 1, "There should be a warning about the redirection.") self.assertIn('Node fetched from source', results['report']['messages'][0]['result'], "Message should be the one about the graph ID change.") self.assertEqual(len(results['graph']), 3) assertion_node = [ n for n in results['graph'] if n['id'] == assertion_data['id'] ][0] self.assertEqual(assertion_node['badge'], 'https://example.org/robotics-badge.json') self.assertEqual(results['report']['validationSubject'], alt_assertion_url)
def test_revoked_badge_marked_invalid(self): input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) revocation_list = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': 'http://example.org/revocationList', 'type': 'RevocationList', 'revokedAssertions': [ {'id': input_assertion['id'], 'revocationReason': 'A good reason, for sure'}, {'id': 'urn:uuid:52e4c6b3-8c13-4fa8-8482-a5cf34ef37a9'}, 'urn:uuid:6deb4a00-ebce-4b28-8cc2-afa705ef7be4' ] } input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['revocationList'] = revocation_list['id'] input_issuer['publicKey'] = input_assertion['verification']['creator'] key = RSA.generate(2048) public_key_pem = key.publickey().export_key() cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': public_key_pem.decode() } set_up_context_mock() for doc in [input_assertion, input_badgeclass, input_issuer, cryptographic_key_doc, revocation_list]: responses.add(responses.GET, doc['id'], json=doc, status=200) signature = jws.sign(input_assertion, key, algorithm='RS256') response = verify(signature, use_cache=False) self.assertFalse(response['report']['valid']) msg = [a for a in response['report']['messages'] if a.get('name') == VERIFY_SIGNED_ASSERTION_NOT_REVOKED][0] self.assertIn('A good reason', msg['result']) # Assert pruning went well to eliminate revocationlist revokedAssertions except for the revoked one rev_list = [n for n in response['graph'] if n.get('id') == revocation_list['id']][0] self.assertEqual(len(rev_list['revokedAssertions']), 1) self.assertEqual(rev_list['revokedAssertions'], [revocation_list['revokedAssertions'][0]])
def test_upgrade_1_1_issuer_in_full_verify_with_redirect(self): setUpContextCache() old_url = "http://example.org/oldissuerurl" data = { "@context": "https://w3id.org/openbadges/v1", "type": "Issuer", "id": "http://example.org/realissuerurl", "description": "Example Badge Issuer", "url": "http://example.org", "email": "*****@*****.**", "name": "Test Issuer", "image": "http://example.org/image" } responses.add(responses.GET, old_url, json=data) responses.add(responses.GET, data['id'], json=data) result = verify(old_url) self.assertTrue(result['report']['valid'])
def test_can_full_verify_jws_signed_assertion(self): """ I can input a JWS string I can extract the Assertion from the input signature string and store it as the canonical version of the Assertion. I can discover and retrieve key information from the Assertion. I can Access the signing key I can verify the key is associated with the listed issuer Profile I can verify the JWS signature has been created by a key trusted to correspond to the issuer Profile Next: I can verify an assertion with an ephemeral embedded badgeclass as well """ input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} set_up_image_mock(u'https://example.org/beths-robot-badge.png') input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) set_up_image_mock(input_badgeclass['image']) input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['publicKey'] = input_assertion['verification']['creator'] key = RSA.generate(2048) public_key_pem = key.publickey().export_key() cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': public_key_pem.decode() } set_up_context_mock() for doc in [input_badgeclass, input_issuer, cryptographic_key_doc]: responses.add(responses.GET, doc['id'], json=doc, status=200) signature = jws.sign(input_assertion, key, algorithm='RS256') response = verify(signature, use_cache=False) self.assertTrue(response['report']['valid'])
def test_can_full_verify_with_revocation_check(self): input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} set_up_image_mock(u'https://example.org/beths-robot-badge.png') input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) set_up_image_mock(input_badgeclass['image']) revocation_list = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': 'http://example.org/revocationList', 'type': 'RevocationList', 'revokedAssertions': []} input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['revocationList'] = revocation_list['id'] input_issuer['publicKey'] = input_assertion['verification']['creator'] key = RSA.generate(2048) public_key_pem = key.publickey().export_key() cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': public_key_pem.decode() } set_up_context_mock() for doc in [input_assertion, input_badgeclass, input_issuer, cryptographic_key_doc, revocation_list]: responses.add(responses.GET, doc['id'], json=doc, status=200) header = {'alg': 'RS256'} payload = json.dumps(input_assertion).encode() signature = jws.sign(input_assertion, key, algorithm='RS256') response = verify(signature, use_cache=False) self.assertTrue(response['report']['valid'])
def test_revoked_badge_marked_invalid(self): input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) revocation_list = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': 'http://example.org/revocationList', 'type': 'RevocationList', 'revokedAssertions': [ {'id': input_assertion['id'], 'revocationReason': 'A good reason, for sure'}, {'id': 'urn:uuid:52e4c6b3-8c13-4fa8-8482-a5cf34ef37a9'}, 'urn:uuid:6deb4a00-ebce-4b28-8cc2-afa705ef7be4' ] } input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['revocationList'] = revocation_list['id'] input_issuer['publicKey'] = input_assertion['verification']['creator'] private_key = RSA.generate(2048) cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': make_string_from_bytes(private_key.publickey().exportKey('PEM')) } set_up_context_mock() for doc in [input_assertion, input_badgeclass, input_issuer, cryptographic_key_doc, revocation_list]: responses.add(responses.GET, doc['id'], json=doc, status=200) header = json.dumps({'alg': 'RS256'}) payload = json.dumps(input_assertion) encoded_separator = '.' if not sys.version[:3] < '3': encoded_separator = '.'.encode() encoded_header = b64encode(header.encode()) encoded_payload = b64encode(payload.encode()) else: encoded_header = b64encode(header) encoded_payload = b64encode(payload) signature = encoded_separator.join([ encoded_header, encoded_payload, jws.sign(header, payload, private_key, is_json=True) ]) response = verify(signature, use_cache=False) self.assertFalse(response['report']['valid']) msg = [a for a in response['report']['messages'] if a.get('name') == VERIFY_SIGNED_ASSERTION_NOT_REVOKED][0] self.assertIn('A good reason', msg['result']) # Assert pruning went well to eliminate revocationlist revokedAssertions except for the revoked one rev_list = [n for n in response['graph'] if n.get('id') == revocation_list['id']][0] self.assertEqual(len(rev_list['revokedAssertions']), 1) self.assertEqual(rev_list['revokedAssertions'], [revocation_list['revokedAssertions'][0]])
def test_can_full_verify_jws_signed_assertion(self): """ I can input a JWS string I can extract the Assertion from the input signature string and store it as the canonical version of the Assertion. I can discover and retrieve key information from the Assertion. I can Access the signing key I can verify the key is associated with the listed issuer Profile I can verify the JWS signature has been created by a key trusted to correspond to the issuer Profile Next: I can verify an assertion with an ephemeral embedded badgeclass as well """ input_assertion = json.loads(test_components['2_0_basic_assertion']) input_assertion['verification'] = {'type': 'signed', 'creator': 'http://example.org/key1'} set_up_image_mock(u'https://example.org/beths-robot-badge.png') input_badgeclass = json.loads(test_components['2_0_basic_badgeclass']) set_up_image_mock(input_badgeclass['image']) input_issuer = json.loads(test_components['2_0_basic_issuer']) input_issuer['publicKey'] = input_assertion['verification']['creator'] private_key = RSA.generate(2048) cryptographic_key_doc = { '@context': OPENBADGES_CONTEXT_V2_URI, 'id': input_assertion['verification']['creator'], 'type': 'CryptographicKey', 'owner': input_issuer['id'], 'publicKeyPem': private_key.publickey().exportKey('PEM').decode() } set_up_context_mock() for doc in [input_assertion, input_badgeclass, input_issuer, cryptographic_key_doc]: responses.add(responses.GET, doc['id'], json=doc, status=200) header = json.dumps({'alg': 'RS256'}) payload = json.dumps(input_assertion) encoded_separator = '.' if not sys.version[:3] < '3': encoded_separator = '.'.encode() encoded_header = b64encode(header.encode()) encoded_payload = b64encode(payload.encode()) else: encoded_header = b64encode(header) encoded_payload = b64encode(payload) signature = encoded_separator.join([ encoded_header, encoded_payload, jws.sign(header,payload,private_key, is_json=True) ]) response = verify(signature, use_cache=False) print("TEST CAN FULLY VERIFY JWS SIGNED ASSERTION : response:") print(response['report']) self.assertTrue(response['report']['valid'])