Skip to content

Commit

Permalink
Signify client code for challenge/response flow. (#82)
Browse files Browse the repository at this point in the history
Signed-off-by: pfeairheller <pfeairheller@gmail.com>
  • Loading branch information
pfeairheller authored Sep 30, 2023
1 parent 415549d commit cf16893
Show file tree
Hide file tree
Showing 9 changed files with 496 additions and 12 deletions.
6 changes: 1 addition & 5 deletions scripts/list_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@
Testing clienting with integration tests that require a running KERIA Cloud Agent
"""

import json
from time import sleep

from keri.app.keeping import Algos
from keri.core.coring import Tiers
from signify.app.clienting import SignifyClient


def list_notifications():
url = "http://localhost:3901"
bran = b'9876543210abcdefghijk'
bran = b'Pwt6yLXRSs7IjZ23tRHIV'
tier = Tiers.low

client = SignifyClient(passcode=bran, tier=tier, url=url)
Expand Down
286 changes: 286 additions & 0 deletions scripts/multisig-holder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
# -*- encoding: utf-8 -*-
"""
SIGNIFY
signify.app.clienting module
Testing clienting with integration tests that require a running KERIA Cloud Agent
"""
import json
from time import sleep

import requests
from keri import kering
from keri.app import signing
from keri.app.keeping import Algos
from keri.core import coring, eventing
from keri.core.coring import Tiers
from keri.help import helping

from signify.app.clienting import SignifyClient


def multisig_holder():
print("Creating issuer agent")
client0 = create_agent(b'Dmopaoe5tANSD8A5rwIhW',
"EGTZsyZyREvrD-swB4US5n-1r7h-40sVPIrmS14ixuoJ",
"EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz")

print("Creating issuer AID")
create_aid(client0, "issuer", "W1OnK0b5rKq6TcKBWhsQa", "ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG")
add_end_role(client0, "issuer")

print("Creating holder1 agent")
client1 = create_agent(b'PoLT1X6fDQliXyCuzCVuv',
"EBqP5_kfQIsBWPWSKOL0iiaDv-nwVvNsN0YHP7SYKK2u",
"ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0")

print("Creating holder1 AID")
create_aid(client1, "holder1", "B-GzoqRMFLGtV0Zy0Jajw", "ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq")
add_end_role(client1, "holder1")

print("Creating holder2 agent")
client2 = create_agent(b'Pwt6yLXRSs7IjZ23tRHIV',
"EA-SUezF76zn7zF7so-T-DF8FsvI9vO1mtOhWjbdRsqK",
"EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb")
print("Creating holder2 AID")
create_aid(client2, "holder2", "AAuXz_5CvLOXMCtZ1prCS", "EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV")
add_end_role(client2, "holder2")

print("Resolving OOBIs")
holder2 = resolve_oobi(client1, "holder2",
"http://127.0.0.1:3902/oobi/EBlzZyyDM2wBzPLPKO0RiMGbYJ1PuryD1-zQOr9fKctV/agent/"
"EBn32S-PTYCVZWIhE4jT0l9-23suzNs2z7raYf0YpOSb")
resolve_oobi(client1, "issuer",
"http://127.0.0.1:3902/oobi/ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG/agent/"
"EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz")

holder1 = resolve_oobi(client2, "holder1",
"http://127.0.0.1:3902/oobi/ENIatcaOLTJ3AMCbv0ZiTXR-2HGrJAwsyXVKhQpwuaIq/agent/"
"ENEDfnaIJyB-ITwEZGv559Mzdk0lNng3UaQKJWzFoTK0")
resolve_oobi(client2, "issuer",
"http://127.0.0.1:3902/oobi/ELTkSY_C70Qj8SbPh7F121Q3iA_zNlt8bS-pzOMiCBgG/agent/"
"EPkVulMF7So04EJqUDmmHu6SkllpbOt-KJOnSwckmXwz")

resolve_oobi(client0, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao")
resolve_oobi(client1, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao")
resolve_oobi(client2, "vc", "http://127.0.0.1:7723/oobi/EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao")

words = client1.challenges().generate()
print(f"Challenging holder1 with {words}")
client2.challenges().respond("holder2", holder1['i'], words)

op = client1.challenges().verify("holder1", holder2['i'], words)
while not op["done"]:
op = client1.operations().get(op['name'])
sleep(1)

exn = coring.Serder(ked=op["response"]['exn'])
print(f"Challenge signed in {exn.said}")
client1.challenges().responded(holder2['i'], exn.said)

states = [holder1, holder2]

member1 = get_aid(client1, "holder1")
member2 = get_aid(client2, "holder2")

op1 = create_multisig(client1, "holder", member1, states)
op2 = create_multisig(client2, "holder", member2, states)

gaid1 = wait_on_operation(client1, op1)
print(f"{gaid1['i']} created for holder1")
gaid2 = wait_on_operation(client2, op2)
print(f"{gaid2['i']} created for holder2")

ghab1 = client1.identifiers().get("holder")
ghab2 = client2.identifiers().get("holder")

stamp = helping.nowIso8601()
add_end_role_multisig(client1, "holder", ghab1, member1, client1.agent.pre, stamp=stamp)
op1 = add_end_role_multisig(client2, "holder", ghab2, member2, client1.agent.pre, stamp=stamp)
add_end_role_multisig(client1, "holder", ghab1, member1, client2.agent.pre, stamp=stamp)
op2 = add_end_role_multisig(client2, "holder", ghab2, member2, client2.agent.pre, stamp=stamp)

while not op1["done"]:
op1 = client1.operations().get(op1['name'])
sleep(1)

while not op2["done"]:
op2 = client1.operations().get(op2['name'])
sleep(1)

holder = resolve_oobi(client0, "holder", "http://127.0.0.1:3902/oobi/EH_axvx0v0gwQaCawqem5u8ZeDKx9TUWKsowTa_xj0yb")

print(holder)
create_credential(client0, holder)


def create_agent(bran, controller, agent):
url = "http://localhost:3901"
tier = Tiers.low
client = SignifyClient(passcode=bran, tier=tier)
assert client.controller == controller

evt, siger = client.ctrl.event()

res = requests.post(url="http://localhost:3903/boot",
json=dict(
icp=evt.ked,
sig=siger.qb64,
stem=client.ctrl.stem,
pidx=1,
tier=client.ctrl.tier))

if res.status_code != requests.codes.accepted:
raise kering.AuthNError(f"unable to initialize cloud agent connection, {res.status_code}, {res.text}")

client.connect(url=url, )
assert client.agent is not None
print("Agent created:")
print(f" Agent: {client.agent.pre} Controller: {client.agent.delpre}")
assert client.agent.pre == agent
assert client.agent.delpre == controller
return client


def create_aid(client, name, bran, expected):
identifiers = client.identifiers()
(_, _, op) = identifiers.create(name, bran=bran)
icp = op["response"]
serder = coring.Serder(ked=icp)
assert serder.pre == expected
print(f"AID Created: {serder.pre}")


def resolve_oobi(client, alias, url):
oobis = client.oobis()
operations = client.operations()

op = oobis.resolve(oobi=url,
alias=alias)

print(f"resolving oobi for {alias}")
while not op["done"]:
op = operations.get(op["name"])
sleep(1)

print("... done")
return op["response"]


def create_multisig(client, name, member, states):
identifiers = client.identifiers()
exchanges = client.exchanges()

icp, isigs, op = identifiers.create(name, algo=Algos.group, mhab=member,
isith=["1/2", "1/2"], nsith=["1/2", "1/2"],
states=states,
rstates=states)

smids = [state['i'] for state in states]
recps = [x['i'] for x in states if x['i'] != member['prefix']]

embeds = dict(
icp=eventing.messagize(serder=icp, sigers=[coring.Siger(qb64=sig) for sig in isigs])
)

exchanges.send(member['name'], "multisig", sender=member, route="/multisig/icp",
payload=dict(gid=icp.pre, smids=smids, rmids=smids),
embeds=embeds, recipients=recps)

return op


def get_aid(client, name):
identifiers = client.identifiers()
return identifiers.get(name)


def wait_on_operation(client, op):
operations = client.operations()
while not op["done"]:
op = operations.get(op["name"])
sleep(1)

return op["response"]


def add_end_role(client, name):
identifiers = client.identifiers()
identifiers.addEndRole(name, eid=client.agent.pre)


def add_end_role_multisig(client, name, ghab, m, eid, stamp=None):
exchanges = client.exchanges()
identifiers = client.identifiers()

rpy, sigs, op = identifiers.addEndRole(name, eid=eid, stamp=stamp)

gstate = ghab["state"]
seal = eventing.SealEvent(i=ghab["prefix"], s=gstate["ee"]["s"], d=gstate["ee"]["d"])
ims = eventing.messagize(serder=rpy, sigers=[coring.Siger(qb64=sig) for sig in sigs], seal=seal)
embeds = dict(
rpy=ims
)

members = identifiers.members(name)
recps = []
for member in members['signing']:
recp = member['aid']
if recp == m['prefix']:
continue

recps.append(recp)

exn, _, _ = exchanges.send(m['name'], "multisig", sender=m, route="/multisig/rpy",
payload=dict(gid=ghab['prefix']),
embeds=embeds, recipients=recps)

return op


def create_credential(client, holder):
registries = client.registries()
identifiers = client.identifiers()
credentials = client.credentials()
operations = client.operations()
ipex = client.ipex()
exchanges = client.exchanges()

issuer = identifiers.get("issuer")

print("Creating vLEI Registry")
_, _, _, op = registries.create(hab=issuer, registryName="vLEI")
while not op["done"]:
op = operations.get(op["name"])
sleep(1)
print("... created")

issuer = identifiers.get("issuer")
registry = registries.get(name="issuer", registryName="vLEI")
data = {
"LEI": "5493001KJTIIGC8Y1R17"
}
creder, iserder, anc, sigs, op = credentials.create(issuer, registry, data=data,
schema="EBfdlu8R27Fbx-ehrqwImnK-8Cm79sqbAQ4MmvEAYqao",
recipient=holder['i'])
print(f"Creating credential {creder.said}")
while not op["done"]:
op = operations.get(op["name"])
sleep(1)
print("... created")

prefixer = coring.Prefixer(qb64=iserder.pre)
seqner = coring.Seqner(sn=iserder.sn)
acdc = signing.serialize(creder, prefixer, seqner, iserder.saider)
iss = registries.serialize(iserder, anc)

grant, sigs, end = ipex.grant(issuer, recp=holder['i'], acdc=acdc,
iss=iss, message="",
anc=eventing.messagize(serder=anc, sigers=[coring.Siger(qb64=sig) for sig in sigs]))
print(f"Sending grant {grant.said}")
exchanges.sendFromEvents("issuer", "credential", grant, sigs, end, [holder['i']])
print("... sent")


if __name__ == "__main__":
multisig_holder()
2 changes: 1 addition & 1 deletion src/signify/app/aiding.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def addEndRole(self, name, *, role=Roles.agent, eid=None, stamp=None):
)

res = self.client.post(f"/identifiers/{name}/endroles", json=json)
return res.json()
return rpy, sigs, res.json()

def sign(self, name, ser):
hab = self.get(name)
Expand Down
77 changes: 77 additions & 0 deletions src/signify/app/challenging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- encoding: utf-8 -*-
"""
SIGNIFY
signify.app.challenging module
"""
from signify.app.clienting import SignifyClient


class Challenges:
""" Challenges domain object """

def __init__(self, client: SignifyClient):
""" Create domain class for working with credentials for a single AID
Parameters:
client (SignifyClient): Signify client class for access resources on a KERIA service instance
"""
self.client = client

def generate(self):
""" Request 12 random word challenge phrase from server
Returns:
list: array of 12 random words
"""

res = self.client.get("/challenges")
resp = res.json()
return resp["words"]

def respond(self, name, recp, words):
hab = self.client.identifiers().get(name)
exchanges = self.client.exchanges()

_, _, res = exchanges.send(name, "challenge", sender=hab, route="/challenge/response",
payload=dict(words=words),
embeds=dict(), recipients=[recp])

return res

def verify(self, name, source, words):
""" Ask Agent to verify a given sender signed the provided words
Parameters:
name (str): human readable name of AID environment
source(str): qb64 AID of source of challenge response to check for
words(list): list of challenge words to check for
"""

json = dict(
words=words
)

res = self.client.post(f"/challenges/{name}/verify/{source}", json=json)
return res.json()

def responded(self, name, source, said):
""" Mark challenge response as signed and accepted
Parameters:
name (str): human readable name of AID environment
source (str): qb64 AID of signer
said (str): qb64 AID of exn message representing the signed response
Returns:
bool: True means successful
"""
json = dict(
said=said
)

res = self.client.post(f"/challenges/{name}/verify/{source}", json=json)
return res.json()
Loading

0 comments on commit cf16893

Please sign in to comment.