Skip to content

Commit

Permalink
exn Message Upgrades. (#559)
Browse files Browse the repository at this point in the history
* Update to sign `exn` messages with full Seal to allow for long living exn messages.  Add sender, recipient and embeds to exn messages to support attaching signed event messages that don't get processed automatically.

* Moving back to protocol based exn messages and away from a message protocol.

* All multisig exn messages (icp, rot, ixn, vcp, iss, rvk) now contain embedded events with persistent exn messages.  Support for loading and verify persisted exn messages.

* Fixing all broken tests and demo scripts.

* Credential issuance now sends the credential embedded in the /credential/issue exn message with transposed signatures instead of streamed over.  There is also now a `kli vc accept` command to allow the issuee of a credential to accept or reject an issue credential.
  • Loading branch information
pfeairheller authored Aug 20, 2023
1 parent 6705bf9 commit 41d842d
Show file tree
Hide file tree
Showing 34 changed files with 1,007 additions and 1,146 deletions.
4 changes: 2 additions & 2 deletions scripts/demo/basic/multisig.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

kli init --name multisig1 --base "${KERI_TEMP_DIR}" --salt 0ACDEyMzQ1Njc4OWxtbm9aBc --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis
kli incept --name multisig1 --base "${KERI_TEMP_DIR}" --alias multisig1 --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-1-sample.json
kli ends add --name multisig1 --alias multisig1 --eid BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM --role mailbox
kli ends add --name multisig1 --base "${KERI_TEMP_DIR}" --alias multisig1 --eid BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM --role mailbox

kli init --name multisig2 --base "${KERI_TEMP_DIR}" --salt 0ACDEyMzQ1Njc4OWdoaWpsaw --nopasscode --config-dir ${KERI_SCRIPT_DIR} --config-file demo-witness-oobis
kli incept --name multisig2 --base "${KERI_TEMP_DIR}" --alias multisig2 --file ${KERI_DEMO_SCRIPT_DIR}/data/multisig-2-sample.json
kli ends add --name multisig2 --alias multisig2 --eid BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX --role mailbox
kli ends add --name multisig2 --base "${KERI_TEMP_DIR}" --alias multisig2 --eid BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX --role mailbox

kli oobi resolve --name multisig1 --base "${KERI_TEMP_DIR}" --oobi-alias multisig2 --oobi http://127.0.0.1:5642/oobi/EJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1
kli oobi resolve --name multisig2 --base "${KERI_TEMP_DIR}" --oobi-alias multisig1 --oobi http://127.0.0.1:5642/oobi/EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4
Expand Down
11 changes: 7 additions & 4 deletions scripts/demo/credentials/single-issuer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ kli oobi resolve --name holder --oobi-alias holder --oobi http://127.0.0.1:7723/
kli vc registry incept --name issuer --alias issuer --registry-name vLEI

kli vc issue --name issuer --alias issuer --registry-name vLEI --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao --recipient ELjSFdrTdCebJlmvbFNX9-TLhR2PO0_60al1kQp5_e6k --data @${KERI_DEMO_SCRIPT_DIR}/data/credential-data.json
sleep 2
kli vc list --name holder --alias holder --poll
SAID=`kli vc list --name holder --alias holder --said --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao`
#sleep 2
kli vc accept --name holder --alias holder --poll --auto

kli vc list --name holder --alias holder

SAID=$(kli vc list --name holder --alias holder --said --schema EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao)

kli vc revoke --name issuer --alias issuer --registry-name vLEI --said ${SAID}
kli vc revoke --name issuer --alias issuer --registry-name vLEI --said "${SAID}"
sleep 2
kli vc list --name holder --alias holder --poll
4 changes: 2 additions & 2 deletions src/keri/app/cli/commands/challenge/respond.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ def respondDo(self, tymth, tock=0.0, **opts):
recp = recp[0]['id']

payload = dict(i=hab.pre, words=self.words)
exn = exchanging.exchange(route="/challenge/response", payload=payload)
ims = hab.endorse(serder=exn, last=True, pipelined=False)
exn, _ = exchanging.exchange(route="/challenge/response", payload=payload, sender=hab.pre)
ims = hab.endorse(serder=exn, last=False, pipelined=False)
del ims[:exn.size]

senderHab = hab.mhab if isinstance(hab, GroupHab) else hab
Expand Down
7 changes: 3 additions & 4 deletions src/keri/app/cli/commands/delegate/confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,15 @@ def confirmDo(self, tymth, tock=0.0):
continue

serder = coring.Serder(raw=msg)
del msg[:serder.size]
ims = bytes(msg[serder.size:])

exn, atc = grouping.multisigInteractExn(hab, aids, [anchor])
exn, atc = grouping.multisigInteractExn(hab, aids, ixn=bytearray(msg))
others = list(oset(hab.smids + (hab.rmids or [])))
# others = list(hab.smids)
others.remove(hab.mhab.pre)

for recpt in others: # send notification to other participants as a signalling mechanism
self.postman.send(src=hab.mhab.pre, dest=recpt, topic="multisig", serder=serder,
attachment=bytearray(msg))
attachment=ims)
self.postman.send(src=hab.mhab.pre, dest=recpt, topic="multisig", serder=exn,
attachment=atc)

Expand Down
10 changes: 5 additions & 5 deletions src/keri/app/cli/commands/multisig/incept.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,22 @@ def inceptDo(self, tymth, tock=0.0):
ghab = self.hby.makeGroupHab(group=self.group, mhab=hab, smids=smids,
rmids=rmids, **self.inits)

evt = ghab.makeOwnInception(allowPartiallySigned=True)
serder = coring.Serder(raw=evt)
del evt[:serder.size]
icp = ghab.makeOwnInception(allowPartiallySigned=True)
serder = coring.Serder(raw=icp)
atc = bytes(icp[serder.size:])

# Create a notification EXN message to send to the other agents
exn, ims = grouping.multisigInceptExn(ghab.mhab,
smids=ghab.smids,
rmids=ghab.rmids,
ked=serder.ked)
icp=icp)
others = list(oset(smids + (rmids or [])))

others.remove(ghab.mhab.pre)

for recpt in others: # this goes to other participants only as a signaling mechanism
self.postman.send(src=ghab.mhab.pre, dest=recpt, topic="multisig", serder=serder,
attachment=bytearray(evt))
attachment=bytearray(atc))
self.postman.send(src=ghab.mhab.pre,
dest=recpt,
topic="multisig",
Expand Down
8 changes: 4 additions & 4 deletions src/keri/app/cli/commands/multisig/interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,16 @@ def interactDo(self, tymth, tock=0.0):

ixn = ghab.interact(data=self.data)
serder = coring.Serder(raw=ixn)
del ixn[:serder.size]
atc = bytes(ixn[serder.size:])

exn, atc = grouping.multisigInteractExn(ghab, aids, self.data)
exn, ims = grouping.multisigInteractExn(ghab, aids, ixn=ixn)
others = list(oset(ghab.smids + (ghab.rmids or [])))
others.remove(ghab.mhab.pre)

for recpt in others: # send notification to other participants as a signalling mechanism
self.postman.send(src=ghab.mhab.pre, dest=recpt, topic="multisig", serder=serder,
attachment=bytearray(ixn))
self.postman.send(src=ghab.mhab.pre, dest=recpt, topic="multisig", serder=exn, attachment=atc)
attachment=bytearray(atc))
self.postman.send(src=ghab.mhab.pre, dest=recpt, topic="multisig", serder=exn, attachment=ims)

prefixer = coring.Prefixer(qb64=ghab.pre)
seqner = coring.Seqner(sn=serder.sn)
Expand Down
8 changes: 2 additions & 6 deletions src/keri/app/cli/commands/multisig/rotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ def rotateDo(self, tymth, tock=0.0, **opts):
case _:
raise kering.ConfigurationError(f"invalid rmid representation {rmid}")


if ghab.mhab.pre not in smids:
raise kering.ConfigurationError(f"{ghab.mhab.pre} not in signing members {smids} for this event")

Expand All @@ -201,15 +200,13 @@ def rotateDo(self, tymth, tock=0.0, **opts):
rot = ghab.rotate(isith=self.isith, nsith=self.nsith,
toad=self.toad, cuts=list(self.cuts), adds=list(self.adds), data=self.data,
verfers=merfers, digers=migers)
rserder = coring.Serder(raw=rot)
del rot[:rserder.size]


rserder = coring.Serder(raw=rot)
# Create a notification EXN message to send to the other agents
exn, ims = grouping.multisigRotateExn(ghab=ghab,
smids=smids,
rmids=rmids,
ked=rserder.ked)
rot=bytearray(rot))
others = list(oset(smids + (rmids or [])))

others.remove(ghab.mhab.pre)
Expand All @@ -224,7 +221,6 @@ def rotateDo(self, tymth, tock=0.0, **opts):
serder=exn,
attachment=bytearray(ims))


self.counselor.start(ghab=ghab, prefixer=prefixer, seqner=seqner, saider=rserder.saider)

while True:
Expand Down
176 changes: 176 additions & 0 deletions src/keri/app/cli/commands/vc/accept.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# -*- encoding: utf-8 -*-
"""
KERI
keri.kli.commands module
"""
import argparse
import datetime
import sys

from hio import help
from hio.base import doing

from keri import kering
from keri.app import indirecting, notifying
from keri.app.cli.common import existing, terming
from keri.core import scheming, parsing
from keri.help import helping
from keri.peer import exchanging
from keri.vc import protocoling, proving
from keri.vdr import credentialing, verifying

logger = help.ogler.getLogger()

parser = argparse.ArgumentParser(description='Accept any newly issued credentials')
parser.set_defaults(handler=lambda args: accept(args),
transferable=True)
parser.add_argument('--name', '-n', help='keystore name and file location of KERI keystore', required=True)
parser.add_argument('--alias', '-a', help='human readable alias for the identifier to whom the credential was issued',
default=None)
parser.add_argument('--base', '-b', help='additional optional prefix to file location of KERI keystore',
required=False, default="")
parser.add_argument('--passcode', '-p', help='22 character encryption passcode for keystore (is not saved)',
dest="bran", default=None) # passcode => bran

parser.add_argument("--verbose", "-V", help="print full JSON of all credentials", action="store_true")
parser.add_argument("--said", "-s", help="display only the SAID of found credentials, one per line.",
action="store_true")
parser.add_argument("--auto", "-Y", help="auto accept any issued credentials non-interactively", action="store_true")
parser.add_argument("--poll", "-P", help="Poll mailboxes for any issued credentials", action="store_true")


def accept(args):
""" Command line list credential registries handler
"""
ld = AcceptDoer(name=args.name,
alias=args.alias,
base=args.base,
bran=args.bran,
poll=args.poll,
verbose=args.verbose,
auto=args.auto,
said=args.said)
return [ld]


class AcceptDoer(doing.DoDoer):

def __init__(self, name, alias, base, bran, poll=False, verbose=False, auto=False, said=False):
self.poll = poll
self.verbose = verbose
self.auto = auto
self.said = said

self.hby = existing.setupHby(name=name, base=base, bran=bran)
if alias is None:
alias = existing.aliasInput(self.hby)

self.hab = self.hby.habByName(alias)
self.notifier = notifying.Notifier(hby=self.hby)
self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base)
self.vry = verifying.Verifier(hby=self.hby, reger=self.rgy.reger)
issueHandler = protocoling.IssueHandler(hby=self.hby, rgy=self.rgy, notifier=self.notifier)
self.exc = exchanging.Exchanger(db=self.hby.db, handlers=[issueHandler])
self.mbx = indirecting.MailboxDirector(hby=self.hby, topics=['/credential'], exc=self.exc, verifier=self.vry)

self.doers = [self.mbx, self.exc]

super(AcceptDoer, self).__init__(doers=self.doers + [doing.doify(self.acceptDo)])

def acceptDo(self, tymth, tock=0.0):
""" Check for any credential messages in mailboxes and list all held credentials
Parameters:
tymth (function): injected function wrapper closure returned by .tymen() of
Tymist instance. Calling tymth() returns associated Tymist .tyme.
tock (float): injected initial tock value
Returns: doifiable Doist compatible generator method
"""
# enter context
self.wind(tymth)
self.tock = tock
_ = (yield self.tock)
if self.poll:
end = helping.nowUTC() + datetime.timedelta(seconds=5)
sys.stdout.write(f"Checking mailboxes for any issued credentials")
sys.stdout.flush()
while helping.nowUTC() < end:
sys.stdout.write(".")
sys.stdout.flush()
if "/credential" in self.mbx.times:
end = self.mbx.times['/credential'] + datetime.timedelta(seconds=5)
yield 1.0
print("\n")

for keys, notice in self.notifier.noter.notes.getItemIter():
attrs = notice.attrs
route = attrs['r']

if route == '/credential/issue':
print("Credential issuance received:")
said = attrs['d']
exn, pathed = exchanging.cloneMessage(self.hby, said)
sad = exn.ked['e']["acdc"]
iss = exn.ked['e']['iss']

schema = sad['s']
scraw = self.mbx.verifier.resolver.resolve(schema)
if not scraw:
raise kering.ConfigurationError("Credential schema {} not found".format(schema))

schemer = scheming.Schemer(raw=scraw)
creder = self.rgy.reger.creds.get(keys=(sad['d'],))
if creder is None:
accepted = f"No {terming.Colors.FAIL}{terming.Symbols.FAILED}{terming.Colors.ENDC}"
else:
accepted = f"Yes {terming.Colors.OKGREEN}{terming.Symbols.CHECKMARK}{terming.Colors.ENDC}"
print(f"Credential {sad['d']}:")
print(f" Type: {schemer.sed['title']}")
print(
f" Status: Issued {terming.Colors.OKGREEN}{terming.Symbols.CHECKMARK}{terming.Colors.ENDC}")
print(f" Issued by {sad['i']}")
print(f" Issued on {iss['dt']}")
print(f" Already accepted? {accepted}")

if creder is not None:
self.deleteNote(keys=keys)
continue

creder = proving.Creder(ked=sad)

if self.auto:
print("Auto accepting credential...")
yes = True
else:
yn = input(f"\nAccept [Y|n]? ")
yes = yn in ('', 'y', 'Y')

if yes:
ims = bytearray(creder.raw) + pathed["acdc"]
parsing.Parser(vry=self.vry).parse(ims=ims)

while not self.rgy.reger.creds.get(keys=creder.saidb):
yield self.tock

print(f"{creder.said} Accepted {terming.Colors.OKGREEN}{terming.Symbols.CHECKMARK}"
f"{terming.Colors.ENDC}")

self.deleteNote(keys=keys)

yield self.tock

self.remove(self.doers)

def deleteNote(self, keys):
if self.auto:
print("\nAuto deleting notification.")
self.notifier.noter.notes.rem(keys=keys)
else:
yn = input(f"\n Delete the notification [Y|n]?")
if yn in ('', 'y', 'Y'):
self.notifier.noter.notes.rem(keys=keys)

27 changes: 24 additions & 3 deletions src/keri/app/cli/commands/vc/registry/incept.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from hio import help
from hio.base import doing

from keri.app import indirecting, habbing, grouping
from keri.app import indirecting, habbing, grouping, forwarding
from keri.app.cli.common import existing
from keri.app.habbing import GroupHab
from keri.vdr import credentialing

logger = help.ogler.getLogger()
Expand Down Expand Up @@ -78,10 +79,11 @@ def __init__(self, name, base, alias, bran, registryName, **kwa):
self.rgy = credentialing.Regery(hby=self.hby, name=name, base=base)
self.hbyDoer = habbing.HaberyDoer(habery=self.hby) # setup doer
counselor = grouping.Counselor(hby=self.hby)
self.postman = forwarding.Poster(hby=self.hby)

mbx = indirecting.MailboxDirector(hby=self.hby, topics=["/receipt", "/multisig", "/replay"])
self.registrar = credentialing.Registrar(hby=self.hby, rgy=self.rgy, counselor=counselor)
doers = [self.hbyDoer, counselor, self.registrar, mbx]
doers = [self.hbyDoer, counselor, self.registrar, self.postman, mbx]
self.toRemove = list(doers)

doers.extend([doing.doify(self.inceptDo, **kwa)])
Expand All @@ -103,7 +105,26 @@ def inceptDo(self, tymth, tock=0.0, **kwa):
_ = (yield self.tock)

hab = self.hby.habByName(self.alias)
registry = self.registrar.incept(name=self.registryName, pre=hab.pre, conf=kwa)
if hab is None:
raise ValueError(f"{self.alias} is not a valid AID alias")

registry, ixn = self.registrar.incept(name=self.registryName, pre=hab.pre, conf=kwa)

if isinstance(hab, GroupHab):
send = input(f"\nSend message to other members of this group AID [Y|n]? ")
if send in ("Y", "y"):

smids = hab.db.signingMembers(pre=hab.pre)
smids.remove(hab.mhab.pre)

for recp in smids: # this goes to other participants only as a signaling mechanism
exn, atc = grouping.multisigRegistryInceptExn(hab=hab.mhab, recipient=recp,
vcp=registry.vcp.raw, ixn=ixn)
self.postman.send(src=hab.mhab.pre,
dest=recp,
topic="multisig",
serder=exn,
attachment=atc)

while not self.registrar.complete(pre=registry.regk, sn=0):
self.rgy.processEscrows()
Expand Down
6 changes: 3 additions & 3 deletions src/keri/app/delegating.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ def delegateRequestExn(hab, delpre, ked, aids=None):
data["aids"] = aids

# Create `exn` peer to peer message to notify other participants UI
exn = exchanging.exchange(route=DelegateRequestHandler.resource, modifiers=dict(),
payload=data)
ims = hab.endorse(serder=exn, last=True, pipelined=False)
exn, _ = exchanging.exchange(route=DelegateRequestHandler.resource, modifiers=dict(),
payload=data, sender=hab.pre)
ims = hab.endorse(serder=exn, last=False, pipelined=False)
del ims[:exn.size]

return exn, ims
Loading

0 comments on commit 41d842d

Please sign in to comment.