Source code for keri.vc.protocoling

# -*- encoding: utf-8 -*-
"""
keri.vc.handling module

"""
import os
from collections import namedtuple
from hio.help import ogler

from ..kering import Vrsn_1_0, Vrsn_2_0, Version

from ..peer import cloneMessage, specialExchange
from ..core import exchange

logger = ogler.getLogger()

Ipexage = namedtuple("Ipexage", 'apply offer agree grant admit spurn')
Ipex = Ipexage(apply="apply", offer="offer", agree="agree", grant="grant", admit="admit", spurn="spurn")
PreviousRoutes = {
    Ipex.offer: (Ipex.apply,),
    Ipex.agree: (Ipex.offer,),
    Ipex.grant: (Ipex.agree,),
    Ipex.admit: (Ipex.grant,),
    Ipex.spurn: (Ipex.apply, Ipex.offer, Ipex.agree, Ipex.grant),
}


[docs] class IpexHandler: """ Processor of `exn` IPEX messages. """
[docs] def __init__(self, resource, hby, notifier): """ Initialize instance Parameters: resource (str): route of messages for this handler hby (Habery): local identifier environment notifier (Notifier): outbound notifications """ self.resource = resource self.hby = hby self.notifier = notifier
[docs] def verify(self, serder, attachments=None): """ Do route specific processsing of IPEX protocol exn messages Parameters: serder (Serder): Serder of the IPEX protocol exn message attachments (list): list of tuples of pather, CESR SAD path attachments to the exn event Returns: bool: True means the exn passed behaviour specific verification for IPEX protocol messages """ route = serder.ked['r'] dig = serder.ked['p'] match route.split("/"): case["", "ipex", Ipex.apply]: if not dig: # Apply messages can only start an IPEX exchange return True case["", "ipex", verb] if verb in (Ipex.offer, Ipex.grant): if not dig: # This is an offer, agree or grant opening an IPEX exchange, no prior return True pserder, _ = cloneMessage(self.hby, said=dig) if pserder is None: # previous reference message does not exist return False proute = pserder.ked['r'] pverb = os.path.basename(os.path.normpath(proute)) # Use established PreviousRoutes to determine if this response is valid if pverb not in PreviousRoutes[verb]: return False return self.response(pserder) is None # Make sure we don't have a response already case["", "ipex", verb] if verb in (Ipex.admit, Ipex.agree, Ipex.spurn): if not dig: # Admit and Spurn messages can NOT start an IPEX exchange return False pserder, _ = cloneMessage(self.hby, said=dig) if pserder is None: # previous reference message does not exist return False proute = pserder.ked['r'] pverb = os.path.basename(os.path.normpath(proute)) # Use established PreviousRoutes to determine if this response is valid if pverb not in PreviousRoutes[verb]: return False return self.response(pserder) is None # Make sure we don't have a response already return False
[docs] def response(self, serder): """ Return the IPEX exn message sent as a response to the provided serder, if any Parameters: serder (Serder): IPEX exn message to check for a response Returns: """ saider = self.hby.db.erpy.get(keys=(serder.said,)) if saider: rserder, _ = cloneMessage(self.hby, saider.qb64) # Clone previous so we reverify the sigs return rserder return None
[docs] def handle(self, serder, attachments=None): """ Do route specific processsing of IPEX protocol exn messages Parameters: serder (Serder): Serder of the IPEX protocol exn message attachments (list): list of tuples of pather, CESR SAD path attachments to the exn event """ attrs = serder.ked["a"] data = dict( r=f"/exn{serder.ked['r']}", d=serder.said, m=attrs["m"] ) self.notifier.add(attrs=data)
[docs] def ipexApplyExn(hab, recp, message, schema, attrs, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Apply for an ACDC Parameters:: hab(Hab): identifier environment for issuer of credential recp (str): qb64 AID of recipient message(str): Human readable message regarding the credential application schema (any): schema or its SAID attrs (any): attribute field label list version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns:: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message, s=schema, a=attrs, i=recp ) exn = exchange(sender=hab.pre, route="/ipex/apply", attributes=data, version=Version, pvrsn=pvrsn, gvrsn=gvrsn) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] #ims.extend(end) return exn, ims
[docs] def ipexOfferExn(hab, message, acdc, apply=None, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Offer a metadata ACDC Parameters: hab(Hab): identifier environment for issuer of credential message(str): Human readable message regarding the credential offer acdc (any): metadata ACDC or its SAID apply (Serder): optional IPEX exn apply message that this offer is response to. version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message ) embeds = dict( acdc=acdc ) kwa = dict() if apply is not None: kwa['prior'] = apply.said exn, end = specialExchange(sender=hab.pre, route="/ipex/offer", attributes=data, embeds=embeds, version=version, pvrsn=pvrsn, gvrsn=gvrsn, **kwa) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] ims.extend(end) return exn, ims
[docs] def ipexAgreeExn(hab, message, offer, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Agree an offer Parameters: hab(Hab): identifier environment for issuer of credential message(str): Human readable message regarding the credential agreement offer (Serder): IPEX exn offer message that this offer is response to. version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message ) exn = exchange(sender=hab.pre, prior=offer.said, route="/ipex/agree", attributes=data, version=version, pvrsn=pvrsn, gvrsn=gvrsn) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] #ims.extend(end) return exn, ims
[docs] def ipexGrantExn(hab, recp, message, acdc, iss=None, anc=None, agree=None, dt=None, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Disclose an ACDC Parameters: hab(Hab): identifier environment for issuer of credential recp (str) qb64 AID of recipient of GRANT message message(str): Human readable message regarding the credential disclosure acdc (bytes): CESR stream of serialized ACDC with attachments iss (bytes): serialized TEL issuance event anc (bytes): serialized anchoring event in the KEL, either ixn or rot agree (Serder): optional IPEX exn agree message that this grant is response to. dt (str): Iso8601 formatted date string to use for this request version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message, i=recp, ) embeds = dict( acdc=acdc, ) if iss is not None: embeds['iss'] = iss if anc is not None: embeds['anc'] = anc kwa = dict() if agree is not None: kwa['prior'] = agree.said exn, end = specialExchange(sender=hab.pre, route="/ipex/grant", stamp=dt, attributes=data, embeds=embeds, version=version, pvrsn=pvrsn, gvrsn=gvrsn, **kwa) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] ims.extend(end) return exn, ims
[docs] def ipexAdmitExn(hab, message, grant, dt=None, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Admit a disclosure Parameters: hab(Hab): identifier environment for issuer of credential message(str): Human readable message regarding the admission grant (Serder): IPEX grant exn message serder dt (str): timestamp version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message, ) exn = exchange(sender=hab.pre, prior=grant.said, route="/ipex/admit", stamp=dt, attributes=data, version=version, pvrsn=pvrsn, gvrsn=gvrsn, ) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] #ims.extend(end) return exn, ims
[docs] def ipexSpurnExn(hab, message, spurned, version=Version, pvrsn=None, gvrsn=Version, framed=True, nested=False, genusify=False): """ Reject an application, offer or agreement Parameters: hab(Hab): identifier environment for issuer of credential message(str): Human readable message regarding the admission spurned (Serder): apply, offer, agree or grant received version (Versionage): KERI protocol default version if psvrsn is None pvrsn (Versionage): KERI protocol version gvrsn (Versionage): CESR Genus version for attachment group codes or nesting group code (useful when serder.gvrsn < 2) gvrsn = max(svrsn, gvrsn) where svrsn = serder.gvrsn if serder.gvrsn else serder.pvrsn framed (bool): True means may assume each message plus its attachments is isolated as frame when parsing so do not need attachment group when messagizing False means may not assume eash message plus its attachments is isolated as frame when parsing so do need attachment group when messagizing nested (bool): True means messagize for non-top level This forces non-native serializion to be embedded in non-native group code False means messagize for top level of stream. This allows bare non-native serialization of message genusify (bool): True means prepend genus version code from gvrsn before serder to override default stream genus version False means do nothing Returns: Serder: credential issuance exn peer to peer message bytes: attachments for exn message """ data = dict( m=message ) exn = exchange(sender=hab.pre, prior=spurned.said, route="/ipex/spurn", attributes=data, version=version, pvrsn=pvrsn, gvrsn=gvrsn, ) ims = hab.endorse(serder=exn, last=False, gvrsn=gvrsn, framed=framed, nested=nested, genusify=genusify) del ims[:exn.size] #ims.extend(end) return exn, ims
[docs] def loadHandlers(hby, exc, notifier): """ Load handlers for the IPEX protocol Parameters: hby (Habery): Database and keystore for environment exc (Exchanger): Peer-to-peer message router notifier (Notifier): outbound notifications """ exc.addHandler(IpexHandler(resource="/ipex/apply", hby=hby, notifier=notifier)) exc.addHandler(IpexHandler(resource="/ipex/offer", hby=hby, notifier=notifier)) exc.addHandler(IpexHandler(resource="/ipex/agree", hby=hby, notifier=notifier)) exc.addHandler(IpexHandler(resource="/ipex/grant", hby=hby, notifier=notifier)) exc.addHandler(IpexHandler(resource="/ipex/admit", hby=hby, notifier=notifier)) exc.addHandler(IpexHandler(resource="/ipex/spurn", hby=hby, notifier=notifier))