tba-profile-haip 0.4.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: tba-profile-haip
3
+ Version: 0.4.0.dev2
4
+ Summary:
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.10,<3.15
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: tba-contracts-identity (==0.4.0.dev2)
14
+ Requires-Dist: tba-protocol-oid4vci (==0.4.0.dev2)
15
+ Requires-Dist: tba-protocol-oid4vp (==0.4.0.dev2)
@@ -0,0 +1,11 @@
1
+ [project]
2
+ name="tba-profile-haip"
3
+ version="0.4.0.dev2"
4
+ requires-python=">=3.10,<3.15"
5
+ license="Apache-2.0"
6
+ dependencies=["tba-protocol-oid4vci==0.4.0.dev2","tba-protocol-oid4vp==0.4.0.dev2","tba-contracts-identity==0.4.0.dev2"]
7
+ [build-system]
8
+ requires=["poetry-core>=1.0.0"]
9
+ build-backend="poetry.core.masonry.api"
10
+ [tool.poetry]
11
+ packages=[{include="tba_profile_haip",from="src"}]
@@ -0,0 +1,51 @@
1
+ """Versioned High Assurance Interoperability Profile ownership."""
2
+
3
+ from tba_contracts_identity.protocol_processing import (
4
+ build_protocol_capability_report,
5
+ )
6
+
7
+ from .bindings import CAPABILITY_REQUIREMENTS
8
+ from .claims import HAIP_PROOF_CLAIM_CLASSES
9
+ from .compatibility import COMPATIBILITY_PATHS, HaipCompatibility, compatibility
10
+ from .errors import HaipComponentVersionError, HaipProfileError, HaipTrustBindingError
11
+ from .features import FEATURES_BY_VERSION, supports
12
+ from .migrations import migrate_configuration
13
+ from .profile import HAIP_CREDENTIAL_FORMATS, HaipConfiguration, configure_haip
14
+ from .schemas import HaipComponentVersions, HaipCredentialFormats
15
+ from .versions import CURRENT_VERSION, VERSION_HISTORY, HaipVersion, select_version
16
+
17
+
18
+ def capability_report() -> dict[str, object]:
19
+ return build_protocol_capability_report(
20
+ protocol="haip",
21
+ revision=CURRENT_VERSION.identifier,
22
+ features=tuple(FEATURES_BY_VERSION[CURRENT_VERSION.identifier]),
23
+ evidence_links=("tests/unit/test_versioned_haip_profile.py",),
24
+ extra_requirements=CAPABILITY_REQUIREMENTS,
25
+ include_default_artifact_requirements=False,
26
+ )
27
+
28
+
29
+ __all__ = [
30
+ "CAPABILITY_REQUIREMENTS",
31
+ "COMPATIBILITY_PATHS",
32
+ "CURRENT_VERSION",
33
+ "FEATURES_BY_VERSION",
34
+ "HAIP_CREDENTIAL_FORMATS",
35
+ "HAIP_PROOF_CLAIM_CLASSES",
36
+ "VERSION_HISTORY",
37
+ "HaipCompatibility",
38
+ "HaipComponentVersionError",
39
+ "HaipComponentVersions",
40
+ "HaipConfiguration",
41
+ "HaipCredentialFormats",
42
+ "HaipProfileError",
43
+ "HaipTrustBindingError",
44
+ "HaipVersion",
45
+ "capability_report",
46
+ "compatibility",
47
+ "configure_haip",
48
+ "migrate_configuration",
49
+ "select_version",
50
+ "supports",
51
+ ]
@@ -0,0 +1,73 @@
1
+ """HAIP profile requirements mapped to composed capabilities."""
2
+
3
+ from tba_contracts_identity.capabilities import ProtocolCapabilityRequirement
4
+
5
+ from .versions import CURRENT_VERSION
6
+
7
+ CAPABILITY_REQUIREMENTS = (
8
+ ProtocolCapabilityRequirement(
9
+ "haip",
10
+ CURRENT_VERSION.identifier,
11
+ "haip-credential-issuance",
12
+ "OID4VCI credential response",
13
+ "digital-credential.issuance",
14
+ "issue",
15
+ "haip-credential",
16
+ ),
17
+ ProtocolCapabilityRequirement(
18
+ "haip",
19
+ CURRENT_VERSION.identifier,
20
+ "haip-wallet-attestation",
21
+ "wallet attestation",
22
+ "digital-credential.issuance",
23
+ "verify_wallet_attestation",
24
+ "verified-wallet-attestation",
25
+ ),
26
+ ProtocolCapabilityRequirement(
27
+ "haip",
28
+ CURRENT_VERSION.identifier,
29
+ "haip-presentation",
30
+ "OID4VP authorization response",
31
+ "digital-credential.presentation",
32
+ "present",
33
+ "haip-presentation-result",
34
+ ),
35
+ ProtocolCapabilityRequirement(
36
+ "haip",
37
+ CURRENT_VERSION.identifier,
38
+ "haip-presentation-verification",
39
+ "SD-JWT VC or mdoc presentation",
40
+ "digital-credential.presentation",
41
+ "verify_presentation",
42
+ "verified-haip-presentation",
43
+ ),
44
+ ProtocolCapabilityRequirement(
45
+ "haip",
46
+ CURRENT_VERSION.identifier,
47
+ "haip-artifact-validation",
48
+ "SD-JWT VC, KB-JWT, mdoc, or attestation artifact",
49
+ "artifact.processing",
50
+ "validate",
51
+ "verified-haip-artifact",
52
+ ),
53
+ ProtocolCapabilityRequirement(
54
+ "haip",
55
+ CURRENT_VERSION.identifier,
56
+ "haip-key-verifier-attestation",
57
+ "key or verifier attestation",
58
+ "attestation.appraisal",
59
+ "verify_evidence",
60
+ "verified-attestation-evidence",
61
+ ),
62
+ ProtocolCapabilityRequirement(
63
+ "haip",
64
+ CURRENT_VERSION.identifier,
65
+ "haip-replay-protection",
66
+ "nonce and transaction binding",
67
+ "security.replay-protection",
68
+ "check_and_reserve",
69
+ "haip:presentation-binding",
70
+ ),
71
+ )
72
+
73
+ __all__ = ["CAPABILITY_REQUIREMENTS"]
@@ -0,0 +1,10 @@
1
+ """Claim families composed by HAIP from OID4VCI and OID4VP."""
2
+
3
+ from tba_protocol_oid4vci.claims import OID4VCI_PROOF_CLAIM_CLASSES
4
+ from tba_protocol_oid4vp.claims import OID4VP_BINDING_CLAIM_CLASSES
5
+
6
+ HAIP_PROOF_CLAIM_CLASSES = tuple(
7
+ dict.fromkeys(OID4VCI_PROOF_CLAIM_CLASSES + OID4VP_BINDING_CLAIM_CLASSES)
8
+ )
9
+
10
+ __all__ = ["HAIP_PROOF_CLAIM_CLASSES"]
@@ -0,0 +1,45 @@
1
+ """HAIP revision compatibility paths."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .versions import CURRENT_VERSION, VERSION_HISTORY
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class HaipCompatibility:
10
+ source: str
11
+ target: str
12
+ compatible: bool
13
+ migration_required: bool
14
+ lossless: bool
15
+
16
+
17
+ COMPATIBILITY_PATHS = (
18
+ HaipCompatibility(
19
+ VERSION_HISTORY[0].identifier,
20
+ CURRENT_VERSION.identifier,
21
+ True,
22
+ True,
23
+ False,
24
+ ),
25
+ HaipCompatibility(
26
+ CURRENT_VERSION.identifier,
27
+ CURRENT_VERSION.identifier,
28
+ True,
29
+ False,
30
+ True,
31
+ ),
32
+ )
33
+
34
+
35
+ def compatibility(
36
+ source: str,
37
+ target: str = CURRENT_VERSION.identifier,
38
+ ) -> HaipCompatibility:
39
+ for path in COMPATIBILITY_PATHS:
40
+ if path.source == source and path.target == target:
41
+ return path
42
+ return HaipCompatibility(source, target, False, False, False)
43
+
44
+
45
+ __all__ = ["COMPATIBILITY_PATHS", "HaipCompatibility", "compatibility"]
@@ -0,0 +1,16 @@
1
+ """HAIP profile composition errors."""
2
+
3
+
4
+ class HaipProfileError(ValueError):
5
+ """Base error for an HAIP configuration or binding failure."""
6
+
7
+
8
+ class HaipComponentVersionError(HaipProfileError):
9
+ """Raised when component protocol revisions are not profile-compatible."""
10
+
11
+
12
+ class HaipTrustBindingError(HaipProfileError):
13
+ """Raised when required wallet/key/verifier trust evidence is unavailable."""
14
+
15
+
16
+ __all__ = ["HaipComponentVersionError", "HaipProfileError", "HaipTrustBindingError"]
@@ -0,0 +1,36 @@
1
+ FEATURES_BY_VERSION = {
2
+ "draft-03": frozenset(
3
+ {
4
+ "oid4vci",
5
+ "oid4vp",
6
+ "sd-jwt-vc",
7
+ "iso-mdoc",
8
+ "wallet-attestation",
9
+ "key-attestation",
10
+ }
11
+ ),
12
+ "1.0": frozenset(
13
+ {
14
+ "oid4vci-1.0",
15
+ "oid4vp-1.0",
16
+ "sd-jwt-vc",
17
+ "iso-mdoc",
18
+ "dcql",
19
+ "wallet-attestation",
20
+ "key-attestation",
21
+ "holder-binding",
22
+ "verifier-attestation",
23
+ "encrypted-responses",
24
+ }
25
+ ),
26
+ }
27
+
28
+
29
+ def supports(feature: str, version: str = "1.0") -> bool:
30
+ try:
31
+ return feature in FEATURES_BY_VERSION[version]
32
+ except KeyError as exc:
33
+ raise ValueError(f"unsupported HAIP version: {version}") from exc
34
+
35
+
36
+ __all__ = ["FEATURES_BY_VERSION", "supports"]
@@ -0,0 +1,21 @@
1
+ from typing import Any, Mapping
2
+
3
+
4
+ def migrate_configuration(
5
+ value: Mapping[str, Any], *, source: str, target: str = "1.0"
6
+ ) -> dict[str, Any]:
7
+ if source == target:
8
+ return dict(value)
9
+ if source != "draft-03" or target != "1.0":
10
+ raise ValueError(f"unsupported HAIP migration: {source} -> {target}")
11
+ migrated = dict(value)
12
+ if "presentation_definition" in migrated:
13
+ raise ValueError(
14
+ "HAIP draft Presentation Exchange constraints require an explicit DCQL migration"
15
+ )
16
+ migrated["oid4vci_version"] = "1.0"
17
+ migrated["oid4vp_version"] = "1.0"
18
+ return migrated
19
+
20
+
21
+ __all__ = ["migrate_configuration"]
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass
2
+
3
+ from tba_protocol_oid4vci import select_version as select_oid4vci_version
4
+ from tba_protocol_oid4vp import select_version as select_oid4vp_version
5
+
6
+ HAIP_CREDENTIAL_FORMATS = frozenset({"dc+sd-jwt", "mso_mdoc"})
7
+
8
+
9
+ @dataclass(frozen=True, slots=True)
10
+ class HaipConfiguration:
11
+ credential_formats: frozenset[str]
12
+ oid4vci_version: str = "1.0"
13
+ oid4vp_version: str = "1.0"
14
+ wallet_attestation: bool = True
15
+ key_attestation: bool = True
16
+
17
+ def validate(self) -> None:
18
+ select_oid4vci_version(self.oid4vci_version)
19
+ select_oid4vp_version(self.oid4vp_version)
20
+ unsupported = self.credential_formats - HAIP_CREDENTIAL_FORMATS
21
+ if unsupported:
22
+ raise ValueError(
23
+ f"unsupported HAIP credential formats: {sorted(unsupported)}"
24
+ )
25
+ if not self.credential_formats:
26
+ raise ValueError("HAIP requires at least one credential format")
27
+
28
+
29
+ def configure_haip(*, credential_formats: frozenset[str]) -> HaipConfiguration:
30
+ configuration = HaipConfiguration(credential_formats)
31
+ configuration.validate()
32
+ return configuration
33
+
34
+
35
+ __all__ = ["HAIP_CREDENTIAL_FORMATS", "HaipConfiguration", "configure_haip"]
@@ -0,0 +1,23 @@
1
+ """Typed HAIP component/profile schema descriptors."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .profile import HAIP_CREDENTIAL_FORMATS, HaipConfiguration
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class HaipComponentVersions:
10
+ oid4vci: str = "1.0"
11
+ oid4vp: str = "1.0"
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class HaipCredentialFormats:
16
+ allowed: frozenset[str] = HAIP_CREDENTIAL_FORMATS
17
+
18
+
19
+ __all__ = [
20
+ "HaipComponentVersions",
21
+ "HaipConfiguration",
22
+ "HaipCredentialFormats",
23
+ ]
@@ -0,0 +1,25 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass(frozen=True, slots=True)
5
+ class HaipVersion:
6
+ identifier: str
7
+ status: str
8
+ published: str
9
+
10
+
11
+ VERSION_HISTORY = (
12
+ HaipVersion("draft-03", "superseded-draft", "2025-03"),
13
+ HaipVersion("1.0", "final", "2025-12-24"),
14
+ )
15
+ CURRENT_VERSION = VERSION_HISTORY[-1]
16
+
17
+
18
+ def select_version(identifier: str = "1.0") -> HaipVersion:
19
+ for version in VERSION_HISTORY:
20
+ if version.identifier == identifier:
21
+ return version
22
+ raise ValueError(f"unsupported HAIP version: {identifier}")
23
+
24
+
25
+ __all__ = ["CURRENT_VERSION", "VERSION_HISTORY", "HaipVersion", "select_version"]