kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
				
				
				
			Basic logic for signing/verifying requests
							rodzic
							
								
									ae65190364
								
							
						
					
					
						commit
						aa7365b71f
					
				| 
						 | 
				
			
			@ -89,6 +89,7 @@ LOCAL_APPS = (
 | 
			
		|||
    'funkwhale_api.music',
 | 
			
		||||
    'funkwhale_api.requests',
 | 
			
		||||
    'funkwhale_api.favorites',
 | 
			
		||||
    'funkwhale_api.federation',
 | 
			
		||||
    'funkwhale_api.radios',
 | 
			
		||||
    'funkwhale_api.history',
 | 
			
		||||
    'funkwhale_api.playlists',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
import factory
 | 
			
		||||
import requests
 | 
			
		||||
import requests_http_signature
 | 
			
		||||
 | 
			
		||||
from funkwhale_api.factories import registry
 | 
			
		||||
 | 
			
		||||
from . import signing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
registry.register(signing.get_key_pair, name='federation.KeyPair')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@registry.register(name='federation.SignatureAuth')
 | 
			
		||||
class SignatureAuthFactory(factory.Factory):
 | 
			
		||||
    algorithm = 'rsa-sha256'
 | 
			
		||||
    key = factory.LazyFunction(lambda: signing.get_key_pair()[0])
 | 
			
		||||
    key_id = factory.Faker('url')
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = requests_http_signature.HTTPSignatureAuth
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@registry.register(name='federation.SignedRequest')
 | 
			
		||||
class SignedRequestFactory(factory.Factory):
 | 
			
		||||
    url = factory.Faker('url')
 | 
			
		||||
    method = 'get'
 | 
			
		||||
    auth = factory.SubFactory(SignatureAuthFactory)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = requests.Request
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,21 @@
 | 
			
		|||
from cryptography.hazmat.primitives import serialization as crypto_serialization
 | 
			
		||||
from cryptography.hazmat.primitives.asymmetric import rsa
 | 
			
		||||
from cryptography.hazmat.backends import default_backend as crypto_default_backend
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_key_pair(size=2048):
 | 
			
		||||
    key = rsa.generate_private_key(
 | 
			
		||||
        backend=crypto_default_backend(),
 | 
			
		||||
        public_exponent=65537,
 | 
			
		||||
        key_size=size
 | 
			
		||||
    )
 | 
			
		||||
    private_key = key.private_bytes(
 | 
			
		||||
        crypto_serialization.Encoding.PEM,
 | 
			
		||||
        crypto_serialization.PrivateFormat.PKCS8,
 | 
			
		||||
        crypto_serialization.NoEncryption())
 | 
			
		||||
    public_key = key.public_key().public_bytes(
 | 
			
		||||
        crypto_serialization.Encoding.PEM,
 | 
			
		||||
        crypto_serialization.PublicFormat.PKCS1
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    return private_key, public_key
 | 
			
		||||
| 
						 | 
				
			
			@ -60,3 +60,5 @@ channels_redis>=2.1,<2.2
 | 
			
		|||
django-cacheops>=4,<4.1
 | 
			
		||||
 | 
			
		||||
daphne==2.0.4
 | 
			
		||||
cryptography>=2,<3
 | 
			
		||||
requests-http-signature==0.0.3
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,11 @@ def cache():
 | 
			
		|||
def factories(db):
 | 
			
		||||
    from funkwhale_api import factories
 | 
			
		||||
    for v in factories.registry.values():
 | 
			
		||||
        v._meta.strategy = factory.CREATE_STRATEGY
 | 
			
		||||
        try:
 | 
			
		||||
            v._meta.strategy = factory.CREATE_STRATEGY
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            # probably not a class based factory
 | 
			
		||||
            pass
 | 
			
		||||
    yield factories.registry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +43,11 @@ def factories(db):
 | 
			
		|||
def nodb_factories():
 | 
			
		||||
    from funkwhale_api import factories
 | 
			
		||||
    for v in factories.registry.values():
 | 
			
		||||
        v._meta.strategy = factory.BUILD_STRATEGY
 | 
			
		||||
        try:
 | 
			
		||||
            v._meta.strategy = factory.BUILD_STRATEGY
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            # probably not a class based factory
 | 
			
		||||
            pass
 | 
			
		||||
    yield factories.registry
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
import cryptography.exceptions
 | 
			
		||||
import io
 | 
			
		||||
import pytest
 | 
			
		||||
import requests_http_signature
 | 
			
		||||
 | 
			
		||||
from funkwhale_api.federation import signing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_can_sign_and_verify_request(factories):
 | 
			
		||||
    private, public = factories['federation.KeyPair']()
 | 
			
		||||
    auth = factories['federation.SignatureAuth'](key=private)
 | 
			
		||||
    request = factories['federation.SignedRequest'](
 | 
			
		||||
        auth=auth
 | 
			
		||||
    )
 | 
			
		||||
    prepared_request = request.prepare()
 | 
			
		||||
    assert 'date' in prepared_request.headers
 | 
			
		||||
    assert 'authorization' in prepared_request.headers
 | 
			
		||||
    assert prepared_request.headers['authorization'].startswith('Signature')
 | 
			
		||||
    assert requests_http_signature.HTTPSignatureAuth.verify(
 | 
			
		||||
        prepared_request,
 | 
			
		||||
        key_resolver=lambda **kwargs: public
 | 
			
		||||
    ) is None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_verify_fails_with_wrong_key(factories):
 | 
			
		||||
    wrong_private, wrong_public = factories['federation.KeyPair']()
 | 
			
		||||
    request = factories['federation.SignedRequest']()
 | 
			
		||||
    prepared_request = request.prepare()
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(cryptography.exceptions.InvalidSignature):
 | 
			
		||||
        requests_http_signature.HTTPSignatureAuth.verify(
 | 
			
		||||
            prepared_request,
 | 
			
		||||
            key_resolver=lambda **kwargs: wrong_public
 | 
			
		||||
        )
 | 
			
		||||
		Ładowanie…
	
		Reference in New Issue