tba-protocol-identity-did-core 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.
- tba_protocol_identity_did_core-0.4.0.dev2/PKG-INFO +13 -0
- tba_protocol_identity_did_core-0.4.0.dev2/pyproject.toml +10 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/__init__.py +20 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/bindings.py +5 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/claims.py +5 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/compatibility.py +3 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/errors.py +8 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/features.py +15 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/migrations.py +4 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/protocol.py +79 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/py.typed +0 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/schemas.py +5 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/validation.py +70 -0
- tba_protocol_identity_did_core-0.4.0.dev2/src/tba_protocol_identity_did_core/versions.py +18 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tba-protocol-identity-did-core
|
|
3
|
+
Version: 0.4.0.dev2
|
|
4
|
+
Summary:
|
|
5
|
+
Requires-Python: >=3.10,<3.15
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Requires-Dist: tba-concrete-did-document (==0.4.0.dev2)
|
|
13
|
+
Requires-Dist: tba-contracts-identity-document (==0.4.0.dev2)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name="tba-protocol-identity-did-core"
|
|
3
|
+
version="0.4.0.dev2"
|
|
4
|
+
requires-python=">=3.10,<3.15"
|
|
5
|
+
dependencies=["tba-concrete-did-document==0.4.0.dev2", "tba-contracts-identity-document==0.4.0.dev2"]
|
|
6
|
+
[build-system]
|
|
7
|
+
requires=["poetry-core>=1.0.0"]
|
|
8
|
+
build-backend="poetry.core.masonry.api"
|
|
9
|
+
[tool.poetry]
|
|
10
|
+
packages=[{include="tba_protocol_identity_did_core",from="src"}]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .compatibility import require_version
|
|
2
|
+
from .features import FEATURES, supports
|
|
3
|
+
from .migrations import migration_path
|
|
4
|
+
from .protocol import DidCoreProtocol
|
|
5
|
+
from .validation import RELATIONSHIPS, validate_did, validate_document
|
|
6
|
+
from .versions import CURRENT_VERSION, VERSION_HISTORY, DidCoreVersion
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"DidCoreProtocol",
|
|
10
|
+
"CURRENT_VERSION",
|
|
11
|
+
"FEATURES",
|
|
12
|
+
"RELATIONSHIPS",
|
|
13
|
+
"VERSION_HISTORY",
|
|
14
|
+
"DidCoreVersion",
|
|
15
|
+
"migration_path",
|
|
16
|
+
"require_version",
|
|
17
|
+
"supports",
|
|
18
|
+
"validate_did",
|
|
19
|
+
"validate_document",
|
|
20
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
FEATURES = frozenset(
|
|
2
|
+
{
|
|
3
|
+
"did-syntax",
|
|
4
|
+
"did-url",
|
|
5
|
+
"verification-method",
|
|
6
|
+
"verification-relationships",
|
|
7
|
+
"services",
|
|
8
|
+
"resolution-metadata",
|
|
9
|
+
"document-metadata",
|
|
10
|
+
}
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def supports(feature: str) -> bool:
|
|
15
|
+
return feature in FEATURES
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""DID Core parsing, resolution, and verification bindings."""
|
|
2
|
+
|
|
3
|
+
from tba_concrete_did_document import DidDocument
|
|
4
|
+
from tba_contracts_identity_document import (
|
|
5
|
+
IdentityDocumentResolverPort,
|
|
6
|
+
IdentityDocumentVerificationRequest,
|
|
7
|
+
IdentityDocumentVerificationResult,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from .validation import validate_document
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DidCoreProtocol:
|
|
14
|
+
def __init__(self, resolver: IdentityDocumentResolverPort | None = None) -> None:
|
|
15
|
+
self._resolver = resolver
|
|
16
|
+
|
|
17
|
+
def parse(
|
|
18
|
+
self,
|
|
19
|
+
representation: bytes | str,
|
|
20
|
+
*,
|
|
21
|
+
media_type: str = "application/did+json",
|
|
22
|
+
) -> DidDocument:
|
|
23
|
+
document = DidDocument.from_representation(
|
|
24
|
+
representation,
|
|
25
|
+
media_type=media_type,
|
|
26
|
+
)
|
|
27
|
+
validate_document(document.content)
|
|
28
|
+
return document
|
|
29
|
+
|
|
30
|
+
def verify(
|
|
31
|
+
self,
|
|
32
|
+
request: IdentityDocumentVerificationRequest,
|
|
33
|
+
/,
|
|
34
|
+
) -> IdentityDocumentVerificationResult:
|
|
35
|
+
document = request.document
|
|
36
|
+
try:
|
|
37
|
+
parsed = self.parse(
|
|
38
|
+
document.representation,
|
|
39
|
+
media_type=document.media_type,
|
|
40
|
+
)
|
|
41
|
+
if (
|
|
42
|
+
request.expected_subject is not None
|
|
43
|
+
and parsed.subject != request.expected_subject
|
|
44
|
+
):
|
|
45
|
+
raise ValueError("DID Document subject mismatch")
|
|
46
|
+
if (
|
|
47
|
+
request.expected_controller is not None
|
|
48
|
+
and parsed.controller != request.expected_controller
|
|
49
|
+
):
|
|
50
|
+
raise ValueError("DID Document controller mismatch")
|
|
51
|
+
except (TypeError, ValueError) as exc:
|
|
52
|
+
return IdentityDocumentVerificationResult(False, reason=str(exc))
|
|
53
|
+
return IdentityDocumentVerificationResult(
|
|
54
|
+
True,
|
|
55
|
+
structural_valid=True,
|
|
56
|
+
controller_authorized=True,
|
|
57
|
+
key_material_valid=True,
|
|
58
|
+
representation_valid=True,
|
|
59
|
+
subject=parsed.subject,
|
|
60
|
+
controller=parsed.controller,
|
|
61
|
+
evidence={"did_core_revision": "1.0"},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def resolve(self, identifier: str, /) -> DidDocument:
|
|
65
|
+
if self._resolver is None:
|
|
66
|
+
raise RuntimeError("DID resolver is not configured")
|
|
67
|
+
document = self._resolver.resolve(identifier)
|
|
68
|
+
result = self.verify(
|
|
69
|
+
IdentityDocumentVerificationRequest(
|
|
70
|
+
document,
|
|
71
|
+
expected_subject=identifier,
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
if not result.valid:
|
|
75
|
+
raise ValueError(result.reason or "resolved DID Document is invalid")
|
|
76
|
+
return self.parse(document.representation, media_type=document.media_type)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = ["DidCoreProtocol"]
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from collections.abc import Mapping, Sequence
|
|
2
|
+
|
|
3
|
+
RELATIONSHIPS = frozenset(
|
|
4
|
+
{
|
|
5
|
+
"authentication",
|
|
6
|
+
"assertionMethod",
|
|
7
|
+
"keyAgreement",
|
|
8
|
+
"capabilityInvocation",
|
|
9
|
+
"capabilityDelegation",
|
|
10
|
+
}
|
|
11
|
+
)
|
|
12
|
+
_KEY_MATERIAL_MEMBERS = frozenset({"publicKeyJwk", "publicKeyMultibase"})
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def validate_did(value: str) -> None:
|
|
16
|
+
if not value.startswith("did:") or value.count(":") < 2:
|
|
17
|
+
raise ValueError("invalid DID syntax")
|
|
18
|
+
method, method_specific_id = value[4:].split(":", 1)
|
|
19
|
+
if not method or not method_specific_id or any(ch.isspace() for ch in value):
|
|
20
|
+
raise ValueError("invalid DID syntax")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_document(document: Mapping[str, object]) -> None:
|
|
24
|
+
identifier = document.get("id")
|
|
25
|
+
if not isinstance(identifier, str):
|
|
26
|
+
raise ValueError("DID Document id is required")
|
|
27
|
+
validate_did(identifier)
|
|
28
|
+
|
|
29
|
+
controllers = document.get("controller", ())
|
|
30
|
+
if isinstance(controllers, str):
|
|
31
|
+
controllers = (controllers,)
|
|
32
|
+
if not isinstance(controllers, Sequence):
|
|
33
|
+
raise ValueError("controller must be a DID or sequence of DIDs")
|
|
34
|
+
for controller in controllers:
|
|
35
|
+
if not isinstance(controller, str):
|
|
36
|
+
raise ValueError("controller must contain DIDs")
|
|
37
|
+
validate_did(controller)
|
|
38
|
+
|
|
39
|
+
methods = document.get("verificationMethod", ())
|
|
40
|
+
if not isinstance(methods, Sequence) or isinstance(methods, (str, bytes)):
|
|
41
|
+
raise ValueError("verificationMethod must be an array")
|
|
42
|
+
known: set[str] = set()
|
|
43
|
+
for method in methods:
|
|
44
|
+
if not isinstance(method, Mapping):
|
|
45
|
+
raise ValueError("verificationMethod entries must be objects")
|
|
46
|
+
method_id = method.get("id")
|
|
47
|
+
method_controller = method.get("controller")
|
|
48
|
+
method_type = method.get("type")
|
|
49
|
+
if not all(
|
|
50
|
+
isinstance(value, str) and value
|
|
51
|
+
for value in (method_id, method_controller, method_type)
|
|
52
|
+
):
|
|
53
|
+
raise ValueError("verificationMethod requires id, controller, and type")
|
|
54
|
+
validate_did(method_controller)
|
|
55
|
+
key_members = _KEY_MATERIAL_MEMBERS.intersection(method)
|
|
56
|
+
if len(key_members) != 1:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"verificationMethod requires exactly one public key representation"
|
|
59
|
+
)
|
|
60
|
+
known.add(method_id)
|
|
61
|
+
|
|
62
|
+
for relationship in RELATIONSHIPS:
|
|
63
|
+
entries = document.get(relationship, ())
|
|
64
|
+
if not isinstance(entries, Sequence) or isinstance(entries, (str, bytes)):
|
|
65
|
+
raise ValueError(f"{relationship} must be an array")
|
|
66
|
+
for entry in entries:
|
|
67
|
+
if isinstance(entry, str) and entry not in known:
|
|
68
|
+
raise ValueError(f"unresolved verification relationship: {entry}")
|
|
69
|
+
if not isinstance(entry, (str, Mapping)):
|
|
70
|
+
raise ValueError(f"invalid {relationship} entry")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass(frozen=True, slots=True)
|
|
5
|
+
class DidCoreVersion:
|
|
6
|
+
identifier: str
|
|
7
|
+
status: str
|
|
8
|
+
published: str
|
|
9
|
+
specification_uri: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
CURRENT_VERSION = DidCoreVersion(
|
|
13
|
+
"DID-Core-1.0",
|
|
14
|
+
"W3C-Recommendation",
|
|
15
|
+
"2022-07-19",
|
|
16
|
+
"https://www.w3.org/TR/did-core/",
|
|
17
|
+
)
|
|
18
|
+
VERSION_HISTORY = (CURRENT_VERSION,)
|