diff --git a/example/satosa/integration_test/main.py b/example/satosa/integration_test/main.py
index a4459d4d..49aabe4c 100644
--- a/example/satosa/integration_test/main.py
+++ b/example/satosa/integration_test/main.py
@@ -6,6 +6,7 @@
from bs4 import BeautifulSoup
from pyeudiw.jwt import DEFAULT_SIG_KTY_MAP
+from pyeudiw.presentation_exchange.schemas.oid4vc_presentation_definition import PresentationDefinition
from pyeudiw.tests.federation.base import (
EXP,
leaf_cred,
@@ -27,10 +28,10 @@
load_specification_from_yaml_string,
issue_sd_jwt,
_adapt_keys,
- import_pyca_pri_rsa
+ import_ec
)
from pyeudiw.storage.db_engine import DBEngine
-from pyeudiw.jwt.utils import unpad_jwt_payload
+from pyeudiw.jwt.utils import decode_jwt_payload
from pyeudiw.tools.utils import iat_now, exp_from_now
from saml2_sp import saml2_request, IDP_BASEURL
@@ -127,7 +128,7 @@
request_uri, verify=False, headers=http_headers)
print(sign_request_obj.json())
-redirect_uri = unpad_jwt_payload(sign_request_obj.json()['response'])[
+redirect_uri = decode_jwt_payload(sign_request_obj.json()['response'])[
'response_uri']
# create a SD-JWT signed by a trusted credential issuer
@@ -191,7 +192,7 @@
aud=str(uuid.uuid4()),
sign_alg=DEFAULT_SIG_KTY_MAP[WALLET_PRIVATE_JWK.key.kty],
holder_key=(
- import_pyca_pri_rsa(
+ import_ec(
WALLET_PRIVATE_JWK.key.priv_key,
kid=WALLET_PRIVATE_JWK.kid
)
@@ -200,7 +201,7 @@
)
)
-red_data = unpad_jwt_payload(sign_request_obj.json()['response'])
+red_data = decode_jwt_payload(sign_request_obj.json()['response'])
req_nonce = red_data['nonce']
data = {
@@ -223,7 +224,10 @@
f'{IDP_BASEURL}/OpenID4VP/.well-known/openid-federation',
verify=False
).content.decode()
-rp_ec = unpad_jwt_payload(rp_ec_jwt)
+rp_ec = decode_jwt_payload(rp_ec_jwt)
+
+presentation_definition = rp_ec["metadata"]["wallet_relying_party"]["presentation_definition"]
+PresentationDefinition(**presentation_definition)
assert redirect_uri == rp_ec["metadata"]['wallet_relying_party']["redirect_uris"][0]
@@ -264,9 +268,12 @@
assert "/saml2" in form["action"]
input_tag = soup.find("input")
assert input_tag["name"] == "SAMLResponse"
-value = BeautifulSoup(base64.b64decode(input_tag["value"]), features="xml")
-attributes = value.find_all("saml:attribute")
+lowered = base64.b64decode(input_tag["value"]).lower()
+value = BeautifulSoup(lowered, features="xml")
+attributes = value.find_all("saml:attribute")
+# expect to have a non-empty list of attributes
+assert attributes
expected = {
# https://oidref.com/2.5.4.42
@@ -280,4 +287,4 @@
value = attribute.contents[0].contents[0]
expected_value = expected.get(name, None)
if expected_value:
- assert value == expected_value
+ assert value == expected_value.lower()
diff --git a/example/satosa/integration_test/metadata/idp.xml b/example/satosa/integration_test/metadata/idp.xml
index f5920160..332747ba 100644
--- a/example/satosa/integration_test/metadata/idp.xml
+++ b/example/satosa/integration_test/metadata/idp.xml
@@ -1 +1 @@
-change with $SATOSA_UI_DISPLAY_NAME_ENchange with $SATOSA_UI_DISPLAY_NAME_ITchange with $SATOSA_UI_DESCRIPTION_ENchange with $SATOSA_UI_DESCRIPTION_ITchange with $SATOSA_UI_LOGO_URLchange with $SATOSA_UI_INFORMATION_URL_ENchange with $SATOSA_UI_INFORMATION_URL_ITchange with $SATOSA_UI_PRIVACY_URL_ENchange with $SATOSA_UI_PRIVACY_URL_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:oasis:names:tc:SAML:2.0:nameid-format:transientchange with $SATOSA_ORGANIZATION_NAME_ENchange with $SATOSA_ORGANIZATION_NAME_ITchange with $SATOSA_ORGANIZATION_DISPLAY_NAME_ENchange with $SAOSA_ORGANIZATION_DISPLAY_NAME_ITchange with $SATOSA_ORGANIZATION_URL_ENchange with $SATOSA_ORGANIZATION_URL_ITchange with $SATOSA_CONTACT_PERSON_GIVEN_NAMEchange with $SATOSA_CONTACT_PERSON_EMAIL_ADDRESS
\ No newline at end of file
+change with $SATOSA_UI_DISPLAY_NAME_ENchange with $SATOSA_UI_DISPLAY_NAME_ITchange with $SATOSA_UI_DESCRIPTION_ENchange with $SATOSA_UI_DESCRIPTION_ITchange with $SATOSA_UI_LOGO_URLchange with $SATOSA_UI_INFORMATION_URL_ENchange with $SATOSA_UI_INFORMATION_URL_ITchange with $SATOSA_UI_PRIVACY_URL_ENchange with $SATOSA_UI_PRIVACY_URL_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:oasis:names:tc:SAML:2.0:nameid-format:transientchange with $SATOSA_ORGANIZATION_NAME_ENchange with $SATOSA_ORGANIZATION_NAME_ITchange with $SATOSA_ORGANIZATION_DISPLAY_NAME_ENchange with $SAOSA_ORGANIZATION_DISPLAY_NAME_ITchange with $SATOSA_ORGANIZATION_URL_ENchange with $SATOSA_ORGANIZATION_URL_ITchange with $SATOSA_CONTACT_PERSON_GIVEN_NAMEchange with $SATOSA_CONTACT_PERSON_EMAIL_ADDRESS
\ No newline at end of file
diff --git a/example/satosa/integration_test/saml2_sp.py b/example/satosa/integration_test/saml2_sp.py
index dfa29564..134b24fc 100644
--- a/example/satosa/integration_test/saml2_sp.py
+++ b/example/satosa/integration_test/saml2_sp.py
@@ -16,7 +16,7 @@
BASE = 'http://pyeudiw_demo.example.org'
BASE_URL = '{}/saml2'.format(BASE)
-IDP_BASEURL = "https://localhost:10000"
+IDP_BASEURL = "https://localhost"
IDP_ENTITYID = f'{IDP_BASEURL}/Saml2IDP/metadata'
SAML_CONFIG = {
diff --git a/example/satosa/integration_test/settings.py b/example/satosa/integration_test/settings.py
index e2a65f3e..ae3ffe9c 100644
--- a/example/satosa/integration_test/settings.py
+++ b/example/satosa/integration_test/settings.py
@@ -12,7 +12,7 @@
from pyeudiw.tools.utils import iat_now, exp_from_now
-RP_EID = "https://localhost:10000/OpenID4VP"
+RP_EID = "https://localhost/OpenID4VP"
CONFIG_DB = {
"mongo_db": {
@@ -110,7 +110,7 @@
]
}
rp_signer = JWS(
- rp_ec, alg="RS256",
+ rp_ec, alg="ES256",
typ="application/entity-statement+jwt"
)
@@ -125,11 +125,11 @@
}
}
ta_signer = JWS(
- _es, alg="RS256",
+ _es, alg="ES256",
typ="application/entity-statement+jwt"
)
its_trust_chain = [
- rp_signer.sign_compact([key_from_jwk_dict(rp_jwks[0])]),
+ rp_signer.sign_compact([key_from_jwk_dict(rp_jwks[1])]),
ta_signer.sign_compact([ta_jwk])
]
diff --git a/example/satosa/pyeudiw_backend.yaml b/example/satosa/pyeudiw_backend.yaml
index d1d9e091..cb59c126 100644
--- a/example/satosa/pyeudiw_backend.yaml
+++ b/example/satosa/pyeudiw_backend.yaml
@@ -168,66 +168,25 @@ config:
# jwks:
#This section contains the details for presentation request
- presentation_definitions:
- - id: pid-sd-jwt:unique_id+given_name+family_name
- input_descriptors:
- - id: pid-sd-jwt:unique_id+given_name+family_name
+ presentation_definition:
+ id: d76c51b7-ea90-49bb-8368-6b3d194fc131
+ input_descriptors:
+ - id: IdentityCredential
format:
- constraints:
- fields:
- - filter:
- const: PersonIdentificationData
+ vc+sd-jwt: { }
+ constraints:
+ limit_disclosure: required
+ fields:
+ - path:
+ - "$.vct"
+ filter:
type: string
- path:
- - $.sd-jwt.type
- - filter:
- type: object
- path:
- - $.sd-jwt.cnf
- - intent_to_retain: 'true'
- path:
- - $.sd-jwt.family_name
- - intent_to_retain: 'true'
- path:
- - $.sd-jwt.given_name
- - intent_to_retain: 'true'
- path:
- - $.sd-jwt.unique_id
- limit_disclosure: required
- jwt:
- alg:
- - EdDSA
- - ES256
- - id: mDL-sample-req
- input_descriptors:
- - format:
- constraints:
- fields:
- - filter:
- const: org.iso.18013.5.1.mDL
- type: string
- path:
- - $.mdoc.doctype
- - filter:
- const: org.iso.18013.5.1
- type: string
- path:
- - $.mdoc.namespace
- - intent_to_retain: 'false'
- path:
- - $.mdoc.family_name
- - intent_to_retain: 'false'
- path:
- - $.mdoc.portrait
- - intent_to_retain: 'false'
- path:
- - $.mdoc.driving_privileges
- limit_disclosure: required
- mso_mdoc:
- alg:
- - EdDSA
- - ES256
- id: mDL
+ const: IdentityCredential
+ - path:
+ - "$.family_name"
+ - path:
+ - "$.given_name"
+
redirect_uris:
- //redirect-uri
diff --git a/example/satosa/static/disco.html b/example/satosa/static/disco.html
index 33df2977..5f1afb47 100644
--- a/example/satosa/static/disco.html
+++ b/example/satosa/static/disco.html
@@ -39,7 +39,7 @@ IT Wallet
-
str:
JWE_CLASS = JWE_RSA
elif isinstance(_key, cryptojwt.jwk.ec.ECKey):
JWE_CLASS = JWE_EC
+ else:
+ raise JWEEncryptionError(f"Error while encrypting: f{_key.__class__.__name__} not supported!")
_payload: str | int | bytes = ""
@@ -92,8 +95,10 @@ def encrypt(self, plain_dict: Union[dict, str, int, None], **kwargs) -> str:
)
if _key.kty == 'EC':
- # TODO - TypeError: key must be bytes-like
- return _keyobj.encrypt(cek=_key.public_key())
+ _keyobj: JWE_EC
+ cek, encrypted_key, iv, params, epk = _keyobj.enc_setup(msg=_payload, key=_key)
+ kwargs = {"params": params, "cek": cek, "iv": iv, "encrypted_key": encrypted_key}
+ return _keyobj.encrypt(**kwargs)
else:
return _keyobj.encrypt(key=_key.public_key())
@@ -121,7 +126,13 @@ def decrypt(self, jwe: str) -> dict:
_decryptor = factory(jwe, alg=_alg, enc=_enc)
_dkey = key_from_jwk_dict(self.jwk.as_dict())
- msg = _decryptor.decrypt(jwe, [_dkey])
+
+ if isinstance(_dkey, cryptojwt.jwk.ec.ECKey):
+ jwdec = JWE_EC()
+ jwdec.dec_setup(_decryptor.jwt, key=self.jwk.key.private_key())
+ msg = jwdec.decrypt(_decryptor.jwt)
+ else:
+ msg = _decryptor.decrypt(jwe, [_dkey])
try:
msg_dict = json.loads(msg)
@@ -157,7 +168,7 @@ def sign(
:param plain_dict: The payload of JWS.
:type plain_dict: Union[dict, str, int, None]
- :param protected: a dict containing all the values
+ :param protected: a dict containing all the values
to include in the protected header.
:type protected: dict
:param kwargs: Other optional fields to generate the JWE.
diff --git a/pyeudiw/jwt/exceptions.py b/pyeudiw/jwt/exceptions.py
index f9428711..dcc5b45f 100644
--- a/pyeudiw/jwt/exceptions.py
+++ b/pyeudiw/jwt/exceptions.py
@@ -5,4 +5,7 @@ class JWTInvalidElementPosition(Exception):
pass
class JWSVerificationError(Exception):
- pass
\ No newline at end of file
+ pass
+
+class JWEEncryptionError(Exception):
+ pass
diff --git a/pyeudiw/oauth2/dpop/schema.py b/pyeudiw/oauth2/dpop/schema.py
index 2674fbfb..22ad3c57 100644
--- a/pyeudiw/oauth2/dpop/schema.py
+++ b/pyeudiw/oauth2/dpop/schema.py
@@ -2,6 +2,8 @@
from pydantic import BaseModel, HttpUrl
+from pyeudiw.jwk.schemas.jwk import JwkSchema
+
class DPoPTokenHeaderSchema(BaseModel):
# header
@@ -17,8 +19,7 @@ class DPoPTokenHeaderSchema(BaseModel):
"PS384",
"PS512",
]
- # TODO - dynamic schemas loader if EC or RSA
- # jwk: JwkSchema
+ jwk: JwkSchema
class DPoPTokenPayloadSchema(BaseModel):
diff --git a/pyeudiw/presentation_exchange/schemas/oid4vc_presentation_definition.py b/pyeudiw/presentation_exchange/schemas/oid4vc_presentation_definition.py
index 74bcb116..ab287aef 100644
--- a/pyeudiw/presentation_exchange/schemas/oid4vc_presentation_definition.py
+++ b/pyeudiw/presentation_exchange/schemas/oid4vc_presentation_definition.py
@@ -114,7 +114,3 @@ class PresentationDefinition(BaseModel):
id: str
input_descriptors: List[InputDescriptor]
submission_requirements: Optional[List[SubmissionRequirement]] = None
-
-
-class PresentationDefinitionForAHighAssuranceProfile(BaseModel):
- presentation_definition: Optional[PresentationDefinition] = None
diff --git a/pyeudiw/presentation_exchange/schemas/presentation_definition.py b/pyeudiw/presentation_exchange/schemas/presentation_definition.py
deleted file mode 100644
index aa064554..00000000
--- a/pyeudiw/presentation_exchange/schemas/presentation_definition.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from typing import Any, Dict, List, Optional
-
-from pydantic import BaseModel
-
-
-class InputDescriptorJwt(BaseModel):
- alg: List[str]
-
-
-class MsoMdoc(BaseModel):
- alg: List[str]
-
-
-class FormatSchema(BaseModel):
- jwt: Optional[InputDescriptorJwt] = None
- mso_mdoc: Optional[MsoMdoc] = None
- constraints: Optional[Dict[str, Any]] = None
-
-
-class InputDescriptor(BaseModel):
- id: str
- name: Optional[str] = None
- purpose: Optional[str] = None
- format: Optional[str | FormatSchema] = None
-
-
-class PresentationDefinition(BaseModel):
- id: str
- input_descriptors: List[InputDescriptor]
diff --git a/pyeudiw/satosa/backend.py b/pyeudiw/satosa/backend.py
index 2cd3f245..f7b57a47 100644
--- a/pyeudiw/satosa/backend.py
+++ b/pyeudiw/satosa/backend.py
@@ -74,12 +74,6 @@ def __init__(
"""
super().__init__(auth_callback_func, internal_attributes, base_url, name)
- try:
- WalletRelyingParty(**config['metadata'])
- except ValidationError as e:
- debug_message = f"""The backend configuration presents the following validation issues: {e}"""
- self._log_warning("OpenID4VPBackend", debug_message)
-
self.config = config
self.client_id = self.config['metadata']['client_id']
self.default_exp = int(self.config['jwt']['default_exp'])
@@ -104,6 +98,11 @@ def __init__(
# resolve metadata pointers/placeholders
self._render_metadata_conf_elements()
self.init_trust_resources()
+ try:
+ WalletRelyingParty(**config['metadata'])
+ except ValidationError as e:
+ debug_message = f"""The backend configuration presents the following validation issues: {e}"""
+ self._log_warning("OpenID4VPBackend", debug_message)
self._log_debug("OpenID4VP init", f"Loaded configuration: {json.dumps(config)}")
def register_endpoints(self) -> list[tuple[str, Callable[[Context], Response]]]:
diff --git a/pyeudiw/sd_jwt/__init__.py b/pyeudiw/sd_jwt/__init__.py
index 14da89ff..3855623b 100644
--- a/pyeudiw/sd_jwt/__init__.py
+++ b/pyeudiw/sd_jwt/__init__.py
@@ -14,6 +14,7 @@
from pyeudiw.jwk import JWK
from pyeudiw.jwt import DEFAULT_SIG_KTY_MAP
from pyeudiw.jwt.utils import decode_jwt_payload
+from pyeudiw.sd_jwt.exceptions import UnknownCurveNistName
from pyeudiw.tools.utils import exp_from_now, iat_now
from jwcrypto.jws import JWS
@@ -107,12 +108,39 @@ def import_pyca_pri_rsa(key, **params):
)
return jwcrypto.jwk.JWK(**params)
+def import_ec(key, **params):
+ pn = key.private_numbers()
+ curve_name = key.curve.name
+ match curve_name:
+ case "secp256r1":
+ nist_name = "P-256"
+ case "secp384r1":
+ nist_name = "P-384"
+ case "secp512r1":
+ nist_name = "P-512"
+ case _:
+ raise UnknownCurveNistName(f"Cannot translate {key.curve.name} into NIST name.")
+ params.update(
+ kty="EC",
+ crv=nist_name,
+ x=pk_encode_int(pn.public_numbers.x),
+ y=pk_encode_int(pn.public_numbers.y),
+ d=pk_encode_int(pn.private_value)
+ )
+ return jwcrypto.jwk.JWK(**params)
def _adapt_keys(issuer_key: JWK, holder_key: JWK):
# _iss_key = issuer_key.key.serialize(private=True)
# _iss_key['key_ops'] = 'sign'
- _issuer_key = import_pyca_pri_rsa(
- issuer_key.key.priv_key, kid=issuer_key.kid)
+
+ match issuer_key.jwk["kty"]:
+ case "RSA":
+ _issuer_key = import_pyca_pri_rsa(
+ issuer_key.key.priv_key, kid=issuer_key.kid)
+ case "EC":
+ _issuer_key = import_ec(issuer_key.key.priv_key, kid=issuer_key.kid)
+ case _:
+ raise KeyError(f"Unsupported 'kty' {issuer_key.key['kty']}")
holder_key = jwcrypto.jwk.JWK.from_json(
json.dumps(_serialize_key(holder_key)))
@@ -138,7 +166,6 @@ def issue_sd_jwt(specification: dict, settings: dict, issuer_key: JWK, holder_ke
specification.update(claims)
use_decoys = specification.get("add_decoy_claims", True)
adapted_keys = _adapt_keys(issuer_key, holder_key)
-
additional_headers = {"trust_chain": trust_chain} if trust_chain else {}
additional_headers['kid'] = issuer_key.kid
diff --git a/pyeudiw/sd_jwt/exceptions.py b/pyeudiw/sd_jwt/exceptions.py
new file mode 100644
index 00000000..d46299db
--- /dev/null
+++ b/pyeudiw/sd_jwt/exceptions.py
@@ -0,0 +1,2 @@
+class UnknownCurveNistName(Exception):
+ pass
diff --git a/pyeudiw/tests/federation/base.py b/pyeudiw/tests/federation/base.py
index 63b34ef9..625e1d9d 100644
--- a/pyeudiw/tests/federation/base.py
+++ b/pyeudiw/tests/federation/base.py
@@ -1,5 +1,5 @@
+from cryptojwt.jwk.ec import new_ec_key
from cryptojwt.jws.jws import JWS
-from cryptojwt.jwk.rsa import new_rsa_key
import json
import pyeudiw.federation.trust_chain_validator as tcv_test
@@ -13,15 +13,18 @@
NOW = iat_now()
EXP = exp_from_now(5000)
+ec_crv = "P-256"
+ec_alg = "ES256"
+
# Define intermediate ec
-intermediate_jwk = new_rsa_key()
+intermediate_jwk = new_ec_key(ec_crv, alg=ec_alg)
# Define TA ec
-ta_jwk = new_rsa_key()
+ta_jwk = new_ec_key(ec_crv, alg=ec_alg)
# Define leaf Credential Issuer
-leaf_cred_jwk = new_rsa_key()
-leaf_cred_jwk_prot = new_rsa_key()
+leaf_cred_jwk = new_ec_key(ec_crv, alg=ec_alg)
+leaf_cred_jwk_prot = new_ec_key(ec_crv, alg=ec_alg)
leaf_cred = {
"exp": EXP,
"iat": NOW,
@@ -62,7 +65,7 @@
intermediate_es_cred["jwks"]['keys'] = [leaf_cred_jwk.serialize()]
# Define leaf Wallet Provider
-leaf_wallet_jwk = new_rsa_key()
+leaf_wallet_jwk = new_ec_key(ec_crv, alg=ec_alg)
leaf_wallet = {
"exp": EXP,
"iat": NOW,
@@ -155,17 +158,17 @@
}
# Sign step
-leaf_cred_signer = JWS(leaf_cred, alg='RS256',
+leaf_cred_signer = JWS(leaf_cred, alg=ec_alg,
typ='entity-statement+jwt')
leaf_cred_signed = leaf_cred_signer.sign_compact([leaf_cred_jwk])
-leaf_wallet_signer = JWS(leaf_wallet, alg='RS256',
+leaf_wallet_signer = JWS(leaf_wallet, alg=ec_alg,
typ='entity-statement+jwt')
leaf_wallet_signed = leaf_wallet_signer.sign_compact([leaf_wallet_jwk])
intermediate_signer_ec = JWS(
- intermediate_ec, alg="RS256",
+ intermediate_ec, alg=ec_alg,
typ="entity-statement+jwt"
)
intermediate_ec_signed = intermediate_signer_ec.sign_compact([
@@ -173,19 +176,19 @@
intermediate_signer_es_cred = JWS(
- intermediate_es_cred, alg='RS256', typ='entity-statement+jwt')
+ intermediate_es_cred, alg=ec_alg, typ='entity-statement+jwt')
intermediate_es_cred_signed = intermediate_signer_es_cred.sign_compact([
intermediate_jwk])
intermediate_signer_es_wallet = JWS(
- intermediate_es_wallet, alg='RS256', typ='entity-statement+jwt')
+ intermediate_es_wallet, alg=ec_alg, typ='entity-statement+jwt')
intermediate_es_wallet_signed = intermediate_signer_es_wallet.sign_compact([
intermediate_jwk])
-ta_es_signer = JWS(ta_es, alg="RS256", typ="entity-statement+jwt")
+ta_es_signer = JWS(ta_es, alg=ec_alg, typ="entity-statement+jwt")
ta_es_signed = ta_es_signer.sign_compact([ta_jwk])
-ta_ec_signer = JWS(ta_ec, alg="RS256", typ="entity-statement+jwt")
+ta_ec_signer = JWS(ta_ec, alg=ec_alg, typ="entity-statement+jwt")
ta_ec_signed = ta_ec_signer.sign_compact([ta_jwk])
diff --git a/pyeudiw/tests/federation/schemas/test_entity_configuration.py b/pyeudiw/tests/federation/schemas/test_entity_configuration.py
index 5592d26d..22788490 100644
--- a/pyeudiw/tests/federation/schemas/test_entity_configuration.py
+++ b/pyeudiw/tests/federation/schemas/test_entity_configuration.py
@@ -66,121 +66,41 @@
]
}
},
- "presentation_definitions": [
- {
- "id": "pid-sd-jwt:unique_id+given_name+family_name",
+ "presentation_definition": {
+ "id": "d76c51b7-ea90-49bb-8368-6b3d194fc131",
"input_descriptors": [
{
- "id": "sd-jwt",
+ "id": "IdentityCredential",
"format": {
- "jwt": {
- "alg": [
- "EdDSA",
- "ES256"
- ]
- },
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.sd-jwt.type"
- ],
- "filter": {
- "type": "string",
- "const": "PersonIdentificationData"
- }
- },
- {
- "path": [
- "$.sd-jwt.cnf"
- ],
- "filter": {
- "type": "object",
- }
- },
- {
- "path": [
- "$.sd-jwt.family_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.given_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.unique_id"
- ],
- "intent_to_retain": "true"
+ "vc+sd-jwt": {}
+ },
+ "constraints": {
+ "limit_disclosure": "required",
+ "fields": [
+ {
+ "path": [
+ "$.vct"
+ ],
+ "filter": {
+ "type": "string",
+ "const": "IdentityCredential"
}
- ]
- }
+ },
+ {
+ "path": [
+ "$.family_name"
+ ]
+ },
+ {
+ "path": [
+ "$.given_name"
+ ]
+ }
+ ]
}
}
]
},
- {
- "id": "mDL-sample-req",
- "input_descriptors": [
- {
- "id": "mDL",
- "format": {
- "mso_mdoc": {
- "alg": [
- "EdDSA",
- "ES256"
- ]
- },
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.mdoc.doctype"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1.mDL"
- }
- },
- {
- "path": [
- "$.mdoc.namespace"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1"
- }
- },
- {
- "path": [
- "$.mdoc.family_name"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.portrait"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.driving_privileges"
- ],
- "intent_to_retain": "false"
- }
- ]
- }
- }
- }
- ]
- }
- ],
"default_max_age": 1111,
diff --git a/pyeudiw/tests/federation/test_schema.py b/pyeudiw/tests/federation/test_schema.py
index 7c5d2d2f..37559f76 100644
--- a/pyeudiw/tests/federation/test_schema.py
+++ b/pyeudiw/tests/federation/test_schema.py
@@ -41,7 +41,40 @@
'request_uris': [],
'redirect_uris': [],
'default_acr_values': [],
- 'presentation_definitions': [],
+ 'presentation_definition': {
+ "id": "d76c51b7-ea90-49bb-8368-6b3d194fc131",
+ "input_descriptors": [
+ {
+ "id": "IdentityCredential",
+ "format": {
+ "vc+sd-jwt": {}
+ },
+ "constraints": {
+ "limit_disclosure": "required",
+ "fields": [
+ {
+ "path": [
+ "$.vct"
+ ],
+ "filter": {
+ "type": "string",
+ "const": "IdentityCredential"
+ }
+ },
+ {
+ "path": [
+ "$.family_name"
+ ]
+ },
+ {
+ "path": [
+ "$.given_name"
+ ]
+ }
+ ]
+ }
+ }
+ ]},
'authorization_signed_response_alg': ['RS256'],
'authorization_encrypted_response_alg': ["RSA-OAEP"],
'authorization_encrypted_response_enc': ["A128CBC-HS256"],
diff --git a/pyeudiw/tests/federation/test_static_trust_chain_validator.py b/pyeudiw/tests/federation/test_static_trust_chain_validator.py
index 15b079e3..1f48df13 100644
--- a/pyeudiw/tests/federation/test_static_trust_chain_validator.py
+++ b/pyeudiw/tests/federation/test_static_trust_chain_validator.py
@@ -37,7 +37,7 @@ def test_is_valid():
invalid_intermediate["jwks"]['keys'] = [invalid_leaf_jwk]
intermediate_signer = JWS(
- invalid_intermediate, alg="RS256",
+ invalid_intermediate, alg="ES256",
typ="application/entity-statement+jwt"
)
invalid_intermediate_es_wallet_signed = intermediate_signer.sign_compact(
@@ -110,7 +110,7 @@ def test_update_st_es_case_source_endpoint():
"source_endpoint": "https://trust-anchor.example.org/fetch"
}
- ta_signer = JWS(ta_es, alg="RS256", typ="application/entity-statement+jwt")
+ ta_signer = JWS(ta_es, alg="ES256", typ="application/entity-statement+jwt")
ta_es_signed = ta_signer.sign_compact([ta_jwk])
def mock_method(*args, **kwargs):
@@ -133,7 +133,7 @@ def test_update_st_es_case_no_source_endpoint():
'jwks': {"keys": []},
}
- ta_signer = JWS(ta_es, alg="RS256", typ="application/entity-statement+jwt")
+ ta_signer = JWS(ta_es, alg="ES256", typ="application/entity-statement+jwt")
ta_es_signed = ta_signer.sign_compact([ta_jwk])
def mock_method_ec(*args, **kwargs):
diff --git a/pyeudiw/tests/openid4vp/schemas/test_schema.py b/pyeudiw/tests/openid4vp/schemas/test_schema.py
index 6496811e..6f63e067 100644
--- a/pyeudiw/tests/openid4vp/schemas/test_schema.py
+++ b/pyeudiw/tests/openid4vp/schemas/test_schema.py
@@ -152,121 +152,41 @@ def test_entity_config_payload():
]
}
},
- "presentation_definitions": [
- {
- "id": "pid-sd-jwt:unique_id+given_name+family_name",
- "input_descriptors": [
- {
- "id": "sd-jwt",
- "format": {
- "jwt": {
- "alg": [
- "EdDSA",
- "ES256"
- ]
+ "presentation_definition": {
+ "id": "d76c51b7-ea90-49bb-8368-6b3d194fc131",
+ "input_descriptors": [
+ {
+ "id": "IdentityCredential",
+ "format": {
+ "vc+sd-jwt": {}
+ },
+ "constraints": {
+ "limit_disclosure": "required",
+ "fields": [
+ {
+ "path": [
+ "$.vct"
+ ],
+ "filter": {
+ "type": "string",
+ "const": "IdentityCredential"
+ }
},
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.sd-jwt.type"
- ],
- "filter": {
- "type": "string",
- "const": "PersonIdentificationData"
- }
- },
- {
- "path": [
- "$.sd-jwt.cnf"
- ],
- "filter": {
- "type": "object"
- }
- },
- {
- "path": [
- "$.sd-jwt.family_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.given_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.unique_id"
- ],
- "intent_to_retain": "true"
- }
- ]
- }
- }
- }
- ]
- },
- {
- "id": "mDL-sample-req",
- "input_descriptors": [
- {
- "id": "mDL",
- "format": {
- "mso_mdoc": {
- "alg": [
- "EdDSA",
- "ES256"
+ {
+ "path": [
+ "$.family_name"
]
},
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.mdoc.doctype"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1.mDL"
- }
- },
- {
- "path": [
- "$.mdoc.namespace"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1"
- }
- },
- {
- "path": [
- "$.mdoc.family_name"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.portrait"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.driving_privileges"
- ],
- "intent_to_retain": "false"
- }
+ {
+ "path": [
+ "$.given_name"
]
}
- }
+ ]
}
- ]
- }
- ],
+ }
+ ]
+ },
"default_max_age": 1111,
"authorization_signed_response_alg": [
"RS256",
diff --git a/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition.py b/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition.py
index 766782ca..40d874ee 100644
--- a/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition.py
+++ b/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition.py
@@ -1,151 +1,12 @@
-import pytest
-from pydantic import ValidationError
+import json
+from pathlib import Path
-from pyeudiw.presentation_exchange.schemas.presentation_definition import PresentationDefinition, InputDescriptor
-
-PID_SD_JWT = {
- "id": "pid-sd-jwt:unique_id+given_name+family_name",
- "input_descriptors": [
- {
- "id": "sd-jwt",
- "format": {
- "jwt": {
- "alg": [
- "EdDSA",
- "ES256"
- ]
- },
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.sd-jwt.type"
- ],
- "filter": {
- "type": "string",
- "const": "PersonIdentificationData"
- }
- },
- {
- "path": [
- "$.sd-jwt.cnf"
- ],
- "filter": {
- "type": "object",
- }
- },
- {
- "path": [
- "$.sd-jwt.family_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.given_name"
- ],
- "intent_to_retain": "true"
- },
- {
- "path": [
- "$.sd-jwt.unique_id"
- ],
- "intent_to_retain": "true"
- }
- ]
- }
- }
- }
- ]
-}
-
-MDL_SAMPLE_REQ = {
- "id": "mDL-sample-req",
- "input_descriptors": [
- {
- "id": "mDL",
- "format": {
- "mso_mdoc": {
- "alg": [
- "EdDSA",
- "ES256"
- ]
- },
- "constraints": {
- "limit_disclosure": "required",
- "fields": [
- {
- "path": [
- "$.mdoc.doctype"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1.mDL"
- }
- },
- {
- "path": [
- "$.mdoc.namespace"
- ],
- "filter": {
- "type": "string",
- "const": "org.iso.18013.5.1"
- }
- },
- {
- "path": [
- "$.mdoc.family_name"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.portrait"
- ],
- "intent_to_retain": "false"
- },
- {
- "path": [
- "$.mdoc.driving_privileges"
- ],
- "intent_to_retain": "false"
- }
- ]
- }
- }
- }
- ]
-}
-
-
-def test_input_descriptor():
- descriptor = PID_SD_JWT["input_descriptors"][0]
- InputDescriptor(**descriptor)
- descriptor["format"]["jwt"]["alg"] = "ES256"
- with pytest.raises(ValidationError):
- InputDescriptor(**descriptor)
- descriptor["format"]["jwt"]["alg"] = ["ES256"]
+from pyeudiw.presentation_exchange.schemas.oid4vc_presentation_definition import \
+ PresentationDefinition
def test_presentation_definition():
- PresentationDefinition(**PID_SD_JWT)
- PresentationDefinition(**MDL_SAMPLE_REQ)
-
- PID_SD_JWT["input_descriptors"][0]["format"]["jwt"]["alg"] = "ES256"
- with pytest.raises(ValidationError):
- PresentationDefinition(**PID_SD_JWT)
-
- PID_SD_JWT["input_descriptors"][0]["format"]["jwt"]["alg"] = ["ES256"]
- PresentationDefinition(**PID_SD_JWT)
-
- del PID_SD_JWT["input_descriptors"][0]["format"]["jwt"]["alg"]
- # alg is an emtpy dict, which is not allowed
- with pytest.raises(ValidationError):
- PresentationDefinition(**PID_SD_JWT)
-
- del PID_SD_JWT["input_descriptors"][0]["format"]["jwt"]
- # since jwt is Optional, this is allowed
- PresentationDefinition(**PID_SD_JWT)
-
- PID_SD_JWT["input_descriptors"][0]["format"]["jwt"] = {"alg": ["ES256"]}
+ p = Path(__file__).with_name('presentation_definition_sd_jwt_vc.json')
+ with open(p) as json_file:
+ data = json.load(json_file)
+ PresentationDefinition(**data)
diff --git a/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition_for_a_high_assurance_profile.py b/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition_for_a_high_assurance_profile.py
deleted file mode 100644
index 258f1ddb..00000000
--- a/pyeudiw/tests/presentation_exchange/schemas/test_presentation_definition_for_a_high_assurance_profile.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import json
-from pathlib import Path
-
-from pyeudiw.presentation_exchange.schemas.oid4vc_presentation_definition import \
- PresentationDefinition
-
-
-def test_presentation_definition():
- p = Path(__file__).with_name('presentation_definition_sd_jwt_vc.json')
- with open(p) as json_file:
- data = json.load(json_file)
- PresentationDefinition(**data)
-
diff --git a/pyeudiw/tests/satosa/test_backend.py b/pyeudiw/tests/satosa/test_backend.py
index bd8eb714..9f64f962 100644
--- a/pyeudiw/tests/satosa/test_backend.py
+++ b/pyeudiw/tests/satosa/test_backend.py
@@ -22,7 +22,7 @@
_adapt_keys,
issue_sd_jwt,
load_specification_from_yaml_string,
- import_pyca_pri_rsa
+ import_ec
)
from pyeudiw.storage.db_engine import DBEngine
from pyeudiw.tools.utils import exp_from_now, iat_now
@@ -188,7 +188,7 @@ def test_vp_validation_in_redirect_endpoint(self, context):
{},
nonce,
str(uuid.uuid4()),
- import_pyca_pri_rsa(holder_jwk.key.priv_key, kid=holder_jwk.kid) if sd_specification.get(
+ import_ec(holder_jwk.key.priv_key, kid=holder_jwk.kid) if sd_specification.get(
"key_binding", False) else None,
sign_alg=DEFAULT_SIG_KTY_MAP[holder_jwk.key.kty],
)
@@ -341,7 +341,7 @@ def test_redirect_endpoint(self, context):
{},
nonce,
str(uuid.uuid4()),
- import_pyca_pri_rsa(holder_jwk.key.priv_key, kid=holder_jwk.kid) if sd_specification.get(
+ import_ec(holder_jwk.key.priv_key, kid=holder_jwk.kid) if sd_specification.get(
"key_binding", False) else None,
sign_alg=DEFAULT_SIG_KTY_MAP[holder_jwk.key.kty],
)
@@ -485,7 +485,7 @@ def test_request_endpoint(self, context):
"sub": self.backend.client_id,
'jwks': self.backend.entity_configuration_as_dict['jwks']
}
- ta_signer = JWS(_es, alg="RS256",
+ ta_signer = JWS(_es, alg="ES256",
typ="application/entity-statement+jwt")
its_trust_chain = [
diff --git a/pyeudiw/tests/test_jwk.py b/pyeudiw/tests/test_jwk.py
index 77f6f391..264d4576 100644
--- a/pyeudiw/tests/test_jwk.py
+++ b/pyeudiw/tests/test_jwk.py
@@ -1,6 +1,8 @@
import pytest
+from pydantic import TypeAdapter
from pyeudiw.jwk import JWK
+from pyeudiw.jwk.schemas.jwk import JwkSchema, ECJwkSchema, RSAJwkSchema
@pytest.mark.parametrize(
@@ -50,3 +52,18 @@ def test_export_public_pem():
jwk_public_pem = jwk.export_public_pem()
assert jwk_public_pem
assert "BEGIN PUBLIC KEY" in jwk_public_pem
+
+
+@pytest.mark.parametrize("key_type", ["EC", "RSA"])
+def test_dynamic_schema_validation(key_type):
+ jwk = JWK(key_type=key_type)
+ model = TypeAdapter(JwkSchema).validate_python(jwk.as_dict())
+ match key_type:
+ case "EC":
+ assert isinstance(model, ECJwkSchema)
+ assert not isinstance(model, RSAJwkSchema)
+ case "RSA":
+ assert isinstance(model, RSAJwkSchema)
+ assert not isinstance(model, ECJwkSchema)
+
+
diff --git a/pyeudiw/tests/test_jwt.py b/pyeudiw/tests/test_jwt.py
index bd9373fc..e89276ed 100644
--- a/pyeudiw/tests/test_jwt.py
+++ b/pyeudiw/tests/test_jwt.py
@@ -19,11 +19,8 @@
JWKs = JWKs_EC + JWKs_RSA
-# TODO: ENC also with EC and not only with RSA
-ENC_JWKs = JWKs_RSA
-
-@pytest.mark.parametrize("jwk, payload", JWKs_RSA)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_decode_jwt_header(jwk, payload):
jwe_helper = JWEHelper(jwk)
jwe = jwe_helper.encrypt(payload)
@@ -42,7 +39,7 @@ def test_jwe_helper_init(key_type):
assert helper.jwk == jwk
-@pytest.mark.parametrize("jwk, payload", ENC_JWKs)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jwe_helper_encrypt(jwk, payload):
helper = JWEHelper(jwk)
jwe = helper.encrypt(payload)
@@ -50,7 +47,7 @@ def test_jwe_helper_encrypt(jwk, payload):
assert is_jwe_format(jwe)
-@pytest.mark.parametrize("jwk, payload", JWKs_RSA)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jwe_helper_decrypt(jwk, payload):
helper = JWEHelper(jwk)
jwe = helper.encrypt(payload)
@@ -61,7 +58,7 @@ def test_jwe_helper_decrypt(jwk, payload):
assert decrypted == payload or decrypted == payload.encode()
-@pytest.mark.parametrize("jwk, payload", ENC_JWKs)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jwe_helper_decrypt_fail(jwk, payload):
helper = JWEHelper(jwk)
jwe = helper.encrypt(payload)
@@ -78,13 +75,14 @@ def test_jws_helper_init(key_type):
assert helper.jwk == jwk
-@pytest.mark.parametrize("jwk, payload", JWKs_RSA)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jws_helper_sign(jwk, payload):
helper = JWSHelper(jwk)
jws = helper.sign(payload)
assert jws
-@pytest.mark.parametrize("jwk, payload", JWKs_RSA)
+
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jws_helper_verify(jwk, payload):
helper = JWSHelper(jwk)
jws = helper.sign(payload)
@@ -95,7 +93,7 @@ def test_jws_helper_verify(jwk, payload):
assert verified == payload or verified == payload.encode()
-@pytest.mark.parametrize("jwk, payload", JWKs_RSA)
+@pytest.mark.parametrize("jwk, payload", JWKs)
def test_jws_helper_verify_fail(jwk, payload):
helper = JWSHelper(jwk)
jws = helper.sign(payload)