From 9a32ad139ea1cd05aac2d9aee737896b8a4a04a1 Mon Sep 17 00:00:00 2001 From: Pasquale De Rose Date: Fri, 3 Nov 2023 10:50:12 +0100 Subject: [PATCH] Feat/features by metadata (#138) * fix: refactoring to WalletRelyingParty class * feat: added print of metadata validation * Moved code relative to openid4vp in the relative package * fix: collapsed multiple warnings in one * fix: moved VP data in __init__.py * fix: removed pformat function --------- Co-authored-by: Giuseppe De Marco --- .../schemas/wallet_relying_party.py | 129 +++++++----------- pyeudiw/openid4vp/schemas/__init__.py | 13 ++ pyeudiw/satosa/backend.py | 11 +- .../tests/openid4vp/schemas/test_schema.py | 9 +- 4 files changed, 71 insertions(+), 91 deletions(-) diff --git a/pyeudiw/federation/schemas/wallet_relying_party.py b/pyeudiw/federation/schemas/wallet_relying_party.py index b84578c5..0eb0cbe9 100644 --- a/pyeudiw/federation/schemas/wallet_relying_party.py +++ b/pyeudiw/federation/schemas/wallet_relying_party.py @@ -1,45 +1,41 @@ -from typing import Any, Dict, List - -from pydantic import BaseModel, HttpUrl, field_validator -from pydantic_core.core_schema import FieldValidationInfo - +from enum import Enum +from typing import Any, List from pyeudiw.jwk.schema import JwksSchema - -_default_algorithms = { - "authorization_signed_response_alg": [ - "RS256", - "ES256" - ], - "authorization_encrypted_response_alg": [ - "RSA-OAEP", - "RSA-OAEP-256", - ], - "authorization_encrypted_response_enc": [ - "A128CBC-HS256", - "A192CBC-HS384", - "A256CBC-HS512", - "A128GCM", - "A192GCM", - "A256GCM", - ], - "id_token_signed_response_alg": [ - "RS256", - "ES256" - ], - "id_token_encrypted_response_alg": [ - "RSA-OAEP", - "RSA-OAEP-256", - ], - "id_token_encrypted_response_enc": [ - "A128CBC-HS256", - "A192CBC-HS384", - "A256CBC-HS512", - "A128GCM", - "A192GCM", - "A256GCM", - ] -} - +from pydantic import BaseModel, HttpUrl, PositiveInt +from pyeudiw.openid4vp.schemas import VPFormat + +class AcrValuesSupported(str, Enum): + spid_l1 = "https://www.spid.gov.it/SpidL1" + spid_l2 = "https://www.spid.gov.it/SpidL2" + spid_l3 = "https://www.spid.gov.it/SpidL3" + +class EncryptionAlgValuesSupported(str, Enum): + rsa_oaep = "RSA-OAEP" + ras_oaep_256 = "RSA-OAEP-256" + ecdh_es = "ECDH-ES" + ecdh_es_a128kw = "ECDH-ES+A128KW" + ecdh_es_a192kw = "ECDH-ES+A192KW" + ecdh_es_a256kw = "ECDH-ES+A256KW" + +class EncryptionEncValuesSupported(str, Enum): + a128cbc_hs256 = "A128CBC-HS256" + a192cbc_hs384 = "A192CBC-HS384" + a256cbc_hs512 = "A256CBC-HS512" + a128gcm = "A128GCM" + a192gcm = "A192GCM" + a256gcm = "A256GCM" + +class SigningAlgValuesSupported(str, Enum): + es256 = "ES256" + es384 = "ES384" + es512 = "ES512" + rs256 = "RS256" + rs384 = "RS384" + rs512 = "RS512" + +class AuthorizationSignedResponseAlg(str, Enum): + rs256 = "RS256" + es256 = "ES256" class WalletRelyingParty(BaseModel): application_type: str @@ -50,48 +46,15 @@ class WalletRelyingParty(BaseModel): request_uris: List[HttpUrl] redirect_uris: List[HttpUrl] default_acr_values: List[HttpUrl] - vp_formats: Dict[str, Dict[str, List[str]]] presentation_definitions: List[Any] - default_max_age: int - authorization_signed_response_alg: List[str] - authorization_encrypted_response_alg: List[str] - authorization_encrypted_response_enc: List[str] + authorization_signed_response_alg: List[AuthorizationSignedResponseAlg] + authorization_encrypted_response_alg: List[EncryptionAlgValuesSupported] + authorization_encrypted_response_enc: List[EncryptionEncValuesSupported] subject_type: str require_auth_time: bool - id_token_signed_response_alg: List[str] - id_token_encrypted_response_alg: List[str] - id_token_encrypted_response_enc: List[str] - - @classmethod - def _get_algorithms_supported(cls, name: str, info: FieldValidationInfo) -> list[str]: - if not info.context: - return _default_algorithms[name] - return info.context.get(name, _default_algorithms[name]) - - @classmethod - def _check_algorithms(cls, algorithms: list[str], name: str, info: FieldValidationInfo): - supported_algorithms = WalletRelyingParty._get_algorithms_supported( - name, info) - for alg in algorithms: - if alg not in supported_algorithms: - raise ValueError( - f"Unsupported algorithm: {alg} for {name}. " - f"Supported algorithms: {supported_algorithms}." - ) - return algorithms - - @field_validator( - "authorization_signed_response_alg", - "authorization_encrypted_response_alg", - "authorization_encrypted_response_enc", - "id_token_signed_response_alg", - "id_token_encrypted_response_alg", - "id_token_encrypted_response_enc" - ) - @classmethod - def check_alg(cls, value, info: FieldValidationInfo): - return WalletRelyingParty._check_algorithms( - value, - info.field_name, - info - ) + id_token_encrypted_response_alg: List[EncryptionAlgValuesSupported] + id_token_encrypted_response_enc: List[EncryptionEncValuesSupported] + id_token_signed_response_alg: List[SigningAlgValuesSupported] + default_acr_values: List[AcrValuesSupported] + default_max_age: PositiveInt + vp_formats: VPFormat \ No newline at end of file diff --git a/pyeudiw/openid4vp/schemas/__init__.py b/pyeudiw/openid4vp/schemas/__init__.py index e69de29b..d3e4a5ca 100644 --- a/pyeudiw/openid4vp/schemas/__init__.py +++ b/pyeudiw/openid4vp/schemas/__init__.py @@ -0,0 +1,13 @@ +from enum import Enum +from typing import List +from pydantic import BaseModel + +class VPSigningAlgResponseSupported(str, Enum): + eddsa = "EdDSA" + es256k = "ES256K" + +class VPAlgorithmSchema(BaseModel): + alg: List[VPSigningAlgResponseSupported] + +class VPFormat(BaseModel): + jwt_vp_json: VPAlgorithmSchema \ No newline at end of file diff --git a/pyeudiw/satosa/backend.py b/pyeudiw/satosa/backend.py index 5bea1add..731aba98 100644 --- a/pyeudiw/satosa/backend.py +++ b/pyeudiw/satosa/backend.py @@ -33,10 +33,10 @@ ) from pyeudiw.storage.db_engine import DBEngine from pyeudiw.storage.exceptions import StorageWriteError +from pyeudiw.federation.schemas.wallet_relying_party import WalletRelyingParty from pydantic import ValidationError - logger = logging.getLogger(__name__) @@ -65,6 +65,15 @@ def __init__(self, auth_callback_func, internal_attributes, config, base_url, na """ super().__init__(auth_callback_func, internal_attributes, base_url, name) + try: + WalletRelyingParty(**config['metadata']) + except ValidationError as e: + logger.warning( + """ + The backend configuration presents the following validation issues: + {} + """.format(logger.warning(e))) + self.config = config self.client_id = self.config['metadata']['client_id'] self.default_exp = int(self.config['jwt']['default_exp']) diff --git a/pyeudiw/tests/openid4vp/schemas/test_schema.py b/pyeudiw/tests/openid4vp/schemas/test_schema.py index 9a3007a3..6496811e 100644 --- a/pyeudiw/tests/openid4vp/schemas/test_schema.py +++ b/pyeudiw/tests/openid4vp/schemas/test_schema.py @@ -274,7 +274,8 @@ def test_entity_config_payload(): ], "authorization_encrypted_response_alg": [ "RSA-OAEP", - "RSA-OAEP-256" + "RSA-OAEP-256", + "ECDH-ES" ], "authorization_encrypted_response_enc": [ "A128CBC-HS256", @@ -318,9 +319,3 @@ def test_entity_config_payload(): ] } EntityConfigurationPayload(**payload) - with pytest.raises(ValidationError): - EntityConfigurationPayload.model_validate( - payload, context={"authorization_encrypted_response_alg": ["ASD"]}) - with pytest.raises(ValidationError): - EntityConfigurationPayload.model_validate( - payload, context={"authorization_encrypted_response_alg": []})