22
33import hashlib
44import re
5- from dataclasses import dataclass , field
5+ import weakref
6+ from dataclasses import dataclass
67from typing import Any
78
89from .contracts import AdvisoryValidationError , validate_advisory_report
@@ -32,12 +33,6 @@ def __init__(self, code: str) -> None:
3233 super ().__init__ (code )
3334
3435
35- @dataclass (frozen = True , slots = True )
36- class _EvidenceSeal :
37- kind : str
38- payload : tuple [object , ...]
39-
40-
4136@dataclass (frozen = True , slots = True )
4237class V1ProvisionalBinding :
4338 period_key : str
@@ -48,7 +43,7 @@ class V1ProvisionalBinding:
4843 status : str = "PROVISIONAL"
4944
5045
51- @dataclass (frozen = True , slots = True )
46+ @dataclass (frozen = True , slots = True , weakref_slot = True )
5247class VerifiedReportEvidence :
5348 period_key : str
5449 as_of : str
@@ -57,7 +52,6 @@ class VerifiedReportEvidence:
5752 fingerprint_version : str
5853 fingerprint_digest : str
5954 status : str = "VERIFIED_REPORT_EVIDENCE"
60- _seal : _EvidenceSeal | None = field (default = None , repr = False , compare = False )
6155
6256
6357@dataclass (frozen = True , slots = True )
@@ -90,11 +84,10 @@ class V2IdentityIndex:
9084 bindings : tuple [V2IdentityBinding , ...]
9185
9286
93- @dataclass (frozen = True , slots = True )
87+ @dataclass (frozen = True , slots = True , weakref_slot = True )
9488class VerifiedIdentityEvidence :
9589 binding : V2IdentityBinding
9690 report_digest : str
97- _seal : _EvidenceSeal | None = field (default = None , repr = False , compare = False )
9891
9992
10093@dataclass (frozen = True , slots = True )
@@ -113,6 +106,31 @@ class AllocatedIdentity:
113106 allocation_source : str
114107
115108
109+ @dataclass (frozen = True , slots = True , weakref_slot = True )
110+ class CompleteVerifiedIdentityInventory :
111+ identities : tuple [VerifiedIdentityEvidence , ...]
112+
113+
114+ _REPORT_CAPABILITIES : dict [int , weakref .ReferenceType [VerifiedReportEvidence ]] = {}
115+ _IDENTITY_CAPABILITIES : dict [int , weakref .ReferenceType [VerifiedIdentityEvidence ]] = {}
116+ _INVENTORY_CAPABILITIES : dict [int , weakref .ReferenceType [CompleteVerifiedIdentityInventory ]] = {}
117+
118+
119+ def _register_capability (registry : dict [int , weakref .ReferenceType [Any ]], value : Any ) -> None :
120+ key = id (value )
121+
122+ def discard (reference : weakref .ReferenceType [Any ], * , key : int = key ) -> None :
123+ if registry .get (key ) is reference :
124+ registry .pop (key , None )
125+
126+ registry [key ] = weakref .ref (value , discard )
127+
128+
129+ def _has_capability (registry : dict [int , weakref .ReferenceType [Any ]], value : Any ) -> bool :
130+ reference = registry .get (id (value ))
131+ return reference is not None and reference () is value
132+
133+
116134def _error (code : str ) -> IdentityMetadataError :
117135 return IdentityMetadataError (code )
118136
@@ -306,8 +324,10 @@ def make_verified_report_evidence(
306324 raise _error ("invalid_schema_version" )
307325 if type (fingerprint_digest ) is not str or re .fullmatch (r"[0-9a-f]{64}" , fingerprint_digest ) is None :
308326 raise _error ("invalid_fingerprint_digest" )
309- payload = (period_key , as_of , cadence , schema_version , FINGERPRINT_VERSION , fingerprint_digest )
310- return VerifiedReportEvidence (* payload , _seal = _EvidenceSeal ("report" , payload ))
327+ return VerifiedReportEvidence (
328+ period_key , as_of , cadence , schema_version , FINGERPRINT_VERSION , fingerprint_digest ,
329+ status = "UNTRUSTED_REPORT_EVIDENCE_CANDIDATE" ,
330+ )
311331
312332
313333def _v1_binding_payload (binding : V1ProvisionalBinding ) -> dict [str , object ]:
@@ -368,14 +388,6 @@ def _compute_report_evidence(report: object, provisional: V1ProvisionalBinding |
368388 return period_key , as_of , cadence , schema_version , digest
369389
370390
371- def _valid_report_seal (evidence : VerifiedReportEvidence ) -> bool :
372- payload = (
373- evidence .period_key , evidence .as_of , evidence .cadence , evidence .schema_version ,
374- evidence .fingerprint_version , evidence .fingerprint_digest ,
375- )
376- return isinstance (evidence ._seal , _EvidenceSeal ) and evidence ._seal == _EvidenceSeal ("report" , payload )
377-
378-
379391def verify_report_evidence (
380392 report : object ,
381393 * ,
@@ -384,14 +396,15 @@ def verify_report_evidence(
384396) -> VerifiedReportEvidence :
385397 period_key , as_of , cadence , schema_version , digest = _compute_report_evidence (report , provisional )
386398 if expected is not None :
387- if not isinstance (expected , VerifiedReportEvidence ) or not _valid_report_seal ( expected ) :
399+ if not isinstance (expected , VerifiedReportEvidence ):
388400 raise _error ("report_evidence_untrusted" )
389401 if (period_key , as_of , cadence , schema_version , digest ) != (
390402 expected .period_key , expected .as_of , expected .cadence , expected .schema_version , expected .fingerprint_digest
391403 ):
392404 raise _error ("identity_content_conflict" )
393- payload = (period_key , as_of , cadence , schema_version , FINGERPRINT_VERSION , digest )
394- return VerifiedReportEvidence (* payload , _seal = _EvidenceSeal ("report" , payload ))
405+ evidence = VerifiedReportEvidence (period_key , as_of , cadence , schema_version , FINGERPRINT_VERSION , digest )
406+ _register_capability (_REPORT_CAPABILITIES , evidence )
407+ return evidence
395408
396409
397410def verify_existing_identity (report : object , binding : V2IdentityBinding ) -> VerifiedIdentityEvidence :
@@ -408,16 +421,40 @@ def verify_existing_identity(report: object, binding: V2IdentityBinding) -> Veri
408421 raise _error ("identity_metadata_mismatch" )
409422 if digest != validated .fingerprint_digest :
410423 raise _error ("identity_content_conflict" )
411- payload = (validated , digest )
412- return VerifiedIdentityEvidence (validated , digest , _EvidenceSeal ("identity" , payload ))
424+ evidence = VerifiedIdentityEvidence (validated , digest )
425+ _register_capability (_IDENTITY_CAPABILITIES , evidence )
426+ return evidence
413427
414428
415429def _valid_identity_evidence (evidence : VerifiedIdentityEvidence ) -> bool :
416- if not isinstance (evidence , VerifiedIdentityEvidence ):
417- return False
418- return isinstance (evidence ._seal , _EvidenceSeal ) and evidence ._seal == _EvidenceSeal (
419- "identity" , (evidence .binding , evidence .report_digest )
420- )
430+ return isinstance (evidence , VerifiedIdentityEvidence ) and _has_capability (_IDENTITY_CAPABILITIES , evidence )
431+
432+
433+ def make_complete_identity_inventory (
434+ index : V2IdentityIndex ,
435+ identities : tuple [VerifiedIdentityEvidence , ...] | list [VerifiedIdentityEvidence ],
436+ ) -> CompleteVerifiedIdentityInventory :
437+ if not isinstance (index , V2IdentityIndex ) or index .schema_version != 2 :
438+ raise _error ("identity_inventory_invalid" )
439+ if any (not _valid_identity_evidence (item ) for item in identities ):
440+ raise _error ("identity_evidence_untrusted" )
441+ bindings = tuple (index .bindings )
442+ try :
443+ _validate_v2_index (bindings )
444+ except IdentityMetadataError :
445+ raise
446+ evidence = tuple (identities )
447+ if len (evidence ) != len (bindings ):
448+ raise _error ("identity_inventory_incomplete" )
449+ expected = sorted (bindings , key = lambda item : (item .period_key , item .json_name , item .html_name ))
450+ actual = sorted ((item .binding for item in evidence ), key = lambda item : (item .period_key , item .json_name , item .html_name ))
451+ if actual != expected :
452+ raise _error ("identity_inventory_incomplete" )
453+ inventory = CompleteVerifiedIdentityInventory (tuple (sorted (evidence , key = lambda item : (
454+ item .binding .period_key , item .binding .json_name , item .binding .html_name
455+ ))))
456+ _register_capability (_INVENTORY_CAPABILITIES , inventory )
457+ return inventory
421458
422459
423460def _new_allocated_identity (evidence : VerifiedReportEvidence , * , canonical : bool , source : str ) -> AllocatedIdentity :
@@ -434,16 +471,21 @@ def _new_allocated_identity(evidence: VerifiedReportEvidence, *, canonical: bool
434471def allocate_identity (
435472 evidence : VerifiedReportEvidence ,
436473 * ,
437- existing_identities : tuple [VerifiedIdentityEvidence , ...] | list [VerifiedIdentityEvidence ] = (),
474+ inventory : CompleteVerifiedIdentityInventory | None = None ,
475+ existing_identities : object = None ,
438476 current_period_key : str | None = None ,
439477) -> AllocatedIdentity :
440- if not isinstance (evidence , VerifiedReportEvidence ) or not _valid_report_seal (evidence ):
478+ if existing_identities is not None :
479+ raise _error ("identity_inventory_required" )
480+ if not isinstance (evidence , VerifiedReportEvidence ) or not _has_capability (_REPORT_CAPABILITIES , evidence ):
441481 raise _error ("report_evidence_untrusted" )
482+ if not isinstance (inventory , CompleteVerifiedIdentityInventory ) or not _has_capability (
483+ _INVENTORY_CAPABILITIES , inventory
484+ ):
485+ raise _error ("identity_inventory_required" )
442486 if current_period_key is not None and not isinstance (current_period_key , str ):
443487 raise _error ("period_mismatch" )
444- identities = tuple (existing_identities )
445- if any (not _valid_identity_evidence (item ) for item in identities ):
446- raise _error ("identity_evidence_untrusted" )
488+ identities = inventory .identities
447489 bindings = tuple (item .binding for item in identities )
448490 try :
449491 _validate_v2_index (bindings )
0 commit comments