async def test_prefetched_sad_not_twice(aiohttp_client): client, auth_man, csc_dummy = await _set_up_dummy_client( aiohttp_client, require_hash_pinning=False) # prefetch SAD that is not bound to any hashes async with client.post('/csc/v1/credentials/authorize', json=auth_man.format_csc_auth_request(), raise_for_status=True) as resp: sad = (await resp.json())['SAD'] auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=auth_man.csc_session_info, credential_info=auth_man.credential_info, csc_auth_info=csc_signer.CSCAuthorizationInfo(sad=sad)) signer = csc_signer.CSCSigner( session=client, auth_manager=auth_man, batch_autocommit=True, batch_size=1, ) result = await signer.async_sign_raw(b'foobar', digest_algorithm='sha256') signer_cert = TESTING_CA.get_cert(CertLabel('signer1')) _validate_raw(result, b'foobar', signer_cert, signature_algorithm=algos.SignedDigestAlgorithm( {'algorithm': 'sha256_rsa'}), md_algorithm='sha256') # but a second attempt should fail with pytest.raises(SigningError, match='No signing results'): await signer.async_sign_raw(b'foobar', digest_algorithm='sha256')
async def test_sign_unreadable_sig(aiohttp_client, response_obj): csc_session_info = csc_signer.CSCServiceSessionInfo('', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'sha256_rsa'}), max_batch_size=1, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) async def fake_return(_request): return web.json_response(response_obj) app = web.Application() app.router.add_post('/csc/v1/signatures/signHash', fake_return) client = await aiohttp_client(app) signer = csc_signer.CSCSigner( client, auth_manager=auth_man, batch_size=1, batch_autocommit=False, client_data='Some client data, because why not') result = asyncio.create_task(signer.async_sign_raw(b'foobarbaz', 'sha256')) with pytest.raises(SigningError, match='Expected response with b64'): await asyncio.sleep(1) await signer.commit() try: result.cancel() await result except asyncio.CancelledError: pass
async def test_sign_mechanism_not_supported(): csc_session_info = csc_signer.CSCServiceSessionInfo( 'https://example.com', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'is_nonsense'}), max_batch_size=10, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) # check expected failure for a signing attempt with pytest.raises(SigningError, match='No signing results available'): async with aiohttp.ClientSession() as session: signer = csc_signer.CSCSigner(session, auth_manager=auth_man) await signer.async_sign_raw(b'foobarbazquux', 'sha256') # check expected failure when fetching the signature mechanism directly with pytest.raises(SigningError, match='must be one of'): # noinspection PyTypeChecker signer = csc_signer.CSCSigner(None, auth_manager=auth_man) signer.get_signature_mechanism(digest_algorithm='sha256') # ...but overrides should still work # noinspection PyTypeChecker signer = csc_signer.CSCSigner(None, auth_manager=auth_man) signer.signature_mechanism = mech \ = algos.SignedDigestAlgorithm({'algorithm': 'sha256_rsa'}) assert signer.get_signature_mechanism(digest_algorithm='sha256') == mech
async def test_csc_placeholder_sig_size(): csc_session_info = csc_signer.CSCServiceSessionInfo( 'https://example.com', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'is_nonsense'}), max_batch_size=10, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) # noinspection PyTypeChecker signer = csc_signer.CSCSigner(None, auth_manager=auth_man) await signer.async_sign_raw(b'foobarbazquux', 'sha256', dry_run=True)
async def test_sign_network_fail(): csc_session_info = csc_signer.CSCServiceSessionInfo( 'https://example.invalid', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'sha256_rsa'}), max_batch_size=10, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) with pytest.raises(SigningError, match='No signing results available'): async with aiohttp.ClientSession() as session: signer = csc_signer.CSCSigner(session, auth_manager=auth_man) await signer.async_sign_raw(b'foobarbazquux', 'sha256')
async def test_fail_different_digest(): csc_session_info = csc_signer.CSCServiceSessionInfo('', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'sha256_rsa'}), max_batch_size=2, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) # noinspection PyTypeChecker signer = csc_signer.CSCSigner(None, auth_manager=auth_man, batch_size=2) with pytest.raises(SigningError, match='same digest function'): result = asyncio.gather( signer.async_sign_raw(b'foobarbaz', 'sha256'), signer.async_sign_raw(b'foobarbazquux', 'sha512'), ) await result
def test_format_csc_auth_request(): # any old auth manager will do auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_signer.CSCServiceSessionInfo( 'https://example.com', 'foobar'), credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset(), max_batch_size=10, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) result = auth_man.format_csc_auth_request( pin='1234', otp='123456', hash_b64s=[ 'Sa6Tcy/PjWP+HM51lmSYLb1bIxYfAH26hWGGKtyW0GM=', 'Sa6Tcy/PjWP+HM51lmSYLb1bIxYfAH26hWGGKtyW0GM=' ], description='baz', client_data='quux') assert result == { 'credentialID': 'foobar', 'numSignatures': 2, 'hash': [ 'Sa6Tcy/PjWP+HM51lmSYLb1bIxYfAH26hWGGKtyW0GM=', 'Sa6Tcy/PjWP+HM51lmSYLb1bIxYfAH26hWGGKtyW0GM=' ], 'PIN': '1234', 'OTP': '123456', 'description': 'baz', 'clientData': 'quux' }
async def test_sign_wrong_number_of_sigs(aiohttp_client): csc_session_info = csc_signer.CSCServiceSessionInfo('', 'foobar') auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=csc_session_info, credential_info=csc_signer.CSCCredentialInfo( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), chain=[], supported_mechanisms=frozenset({'sha256_rsa'}), max_batch_size=2, hash_pinning_required=False, response_data={}), csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) async def fake_return(_request): return web.json_response( {'signatures': [ base64.b64encode(bytes(512)).decode('ascii'), ]}) app = web.Application() app.router.add_post('/csc/v1/signatures/signHash', fake_return) client = await aiohttp_client(app) signer = csc_signer.CSCSigner(client, auth_manager=auth_man, batch_size=2, batch_autocommit=False) result = asyncio.gather( signer.async_sign_raw(b'foobarbaz', 'sha256'), signer.async_sign_raw(b'foobarbazquux', 'sha256'), ) with pytest.raises(SigningError, match='Expected 2 signatures'): await asyncio.sleep(1) await signer.commit() try: result.cancel() await result except asyncio.CancelledError: pass
async def test_multi_commit_failure(aiohttp_client): client, auth_man, csc_dummy = await _set_up_dummy_client(aiohttp_client) # deliberately pass a bogus SAD to make the commit fail auth_man = csc_signer.PrefetchedSADAuthorizationManager( csc_session_info=auth_man.csc_session_info, credential_info=auth_man.credential_info, csc_auth_info=csc_signer.CSCAuthorizationInfo(sad='')) class SlowCommitter(csc_signer.CSCSigner): async def _do_commit(self, batch): # waste time await asyncio.sleep(3) await super()._do_commit(batch) signer = SlowCommitter(session=client, auth_manager=auth_man, batch_autocommit=False, batch_size=1) async def produce_signature(): result = asyncio.create_task( signer.async_sign_raw(b'foobar', 'sha256'), ) await asyncio.sleep(1) with pytest.raises(SigningError, match='Signature request failed'): await signer.commit() with pytest.raises(SigningError, match='No signing results'): return await result async def commit_again(): with pytest.raises(SigningError, match='Commit failed'): await asyncio.sleep(2) await signer.commit() await asyncio.gather(produce_signature(), commit_again()) # this should now return immediately as there is no batch await signer.commit()