forked from hyperledger/indy-node
-
Notifications
You must be signed in to change notification settings - Fork 0
/
agent_prover.py
202 lines (166 loc) · 7.87 KB
/
agent_prover.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import asyncio
import json
from typing import Any
from collections import OrderedDict
from plenum.common.constants import NONCE, TYPE, IDENTIFIER, DATA
from plenum.common.types import f
from plenum.common.util import getCryptonym
from anoncreds.protocol.prover import Prover
from anoncreds.protocol.types import SchemaKey, ID, Claims, ClaimAttributeValues, ProofRequest
from sovrin_client.agent.msg_constants import CLAIM_REQUEST, PROOF, CLAIM_FIELD, \
CLAIM_REQ_FIELD, PROOF_FIELD, \
REQ_AVAIL_CLAIMS, ISSUER_DID, SCHEMA_SEQ_NO, PROOF_REQUEST_FIELD
from sovrin_client.client.wallet.connection import Connection
from sovrin_common.exceptions import LinkNotReady
class AgentProver:
def __init__(self, prover: Prover):
self.prover = prover
def sendRequestForAvailClaims(self, link: Connection):
if self.loop.is_running():
self.loop.call_soon(asyncio.ensure_future,
self.sendRequestForAvailClaimsAsync(link))
else:
self.loop.run_until_complete(
self.sendRequestForAvailClaimsAsync(link))
async def sendRequestForAvailClaimsAsync(self, link: Connection):
op = {
TYPE: REQ_AVAIL_CLAIMS,
NONCE: link.request_nonce
}
try:
self.signAndSendToLink(msg=op, linkName=link.name)
except LinkNotReady as ex:
self.notifyMsgListener(str(ex))
def sendReqClaim(self, link: Connection, schemaKey):
if self.loop.is_running():
self.loop.call_soon(asyncio.ensure_future,
self.send_claim(link, schemaKey))
else:
self.loop.run_until_complete(
self.send_claim(link, schemaKey))
# async def send_claim(self, link, claim_to_request):
# return await self.sendReqClaimAsync(link, claim_to_request)
async def send_claim(self, link: Connection, schema_key):
name, version, origin = schema_key
schema_key = SchemaKey(name, version, origin)
claimReq = await self.prover.createClaimRequest(
schemaId=ID(schema_key),
proverId=link.request_nonce,
reqNonRevoc=False)
# It has served its purpose by this point. Claim Requests do not need a nonce.
schema = await self.prover.wallet.getSchema(ID(schema_key))
claimRequestDetails = {
SCHEMA_SEQ_NO: schema.seqId,
ISSUER_DID: origin,
CLAIM_REQ_FIELD: claimReq.to_str_dict()
}
op = {
TYPE: CLAIM_REQUEST,
NONCE: link.request_nonce,
DATA: claimRequestDetails
}
self.signAndSendToLink(msg=op, linkName=link.name)
def handleProofRequest(self, msg):
body, _ = msg
link = self._getLinkByTarget(getCryptonym(body.get(IDENTIFIER)))
proofRequest = body.get(PROOF_REQUEST_FIELD)
proofRequest = ProofRequest.from_str_dict(proofRequest)
proofReqExist = False
for request in link.proofRequests:
if request.name == proofRequest.name:
proofReqExist = True
break
self.notifyMsgListener(' Proof request {} received from {}.\n'
.format(proofRequest.name, link.name))
if not proofReqExist:
link.proofRequests.append(proofRequest)
else:
self.notifyMsgListener(' Proof request {} already exist.\n'
.format(proofRequest.name))
async def handleReqClaimResponse(self, msg):
body, _ = msg
issuerId = body.get(IDENTIFIER)
claim = body[DATA]
li = self._getLinkByTarget(getCryptonym(issuerId))
if li:
schemaId = ID(schemaId=claim[SCHEMA_SEQ_NO])
schema = await self.prover.wallet.getSchema(schemaId)
self.notifyResponseFromMsg(li.name, body.get(f.REQ_ID.nm))
self.notifyMsgListener(' Received claim "{}".\n'.format(schema.name))
pk = await self.prover.wallet.getPublicKey(schemaId)
claim_attributes = {k: ClaimAttributeValues.from_str_dict(v) for k, v in json.loads(claim[CLAIM_FIELD]).items()}
claim_signature = Claims.from_str_dict(claim[f.SIG.nm], pk.N)
await self.prover.processClaim(schemaId, claim_attributes, claim_signature)
else:
self.notifyMsgListener("No matching connection found")
def sendProof(self, link: Connection, proofReq: ProofRequest):
if self.loop.is_running():
self.loop.call_soon(asyncio.ensure_future,
self.sendProofAsync(link, proofReq))
else:
self.loop.run_until_complete(self.sendProofAsync(link, proofReq))
async def sendProofAsync(self, link: Connection, proofRequest: ProofRequest):
# TODO _F_ this nonce should be from the Proof Request, not from an
# invitation
# TODO rename presentProof to buildProof or generateProof
proof = await self.prover.presentProof(proofRequest)
proof.requestedProof.self_attested_attrs.update(proofRequest.selfAttestedAttrs)
op = {
TYPE: PROOF,
NONCE: link.request_nonce,
PROOF_FIELD: proof.to_str_dict(),
PROOF_REQUEST_FIELD: proofRequest.to_str_dict()
}
self.signAndSendToLink(msg=op, linkName=link.name)
def handleProofStatusResponse(self, msg: Any):
body, _ = msg
data = body.get(DATA)
identifier = body.get(IDENTIFIER)
li = self._getLinkByTarget(getCryptonym(identifier))
self.notifyResponseFromMsg(li.name, body.get(f.REQ_ID.nm))
self.notifyMsgListener(data)
async def getMatchingConnectionsWithReceivedClaimAsync(self, claimName=None):
matchingLinkAndAvailableClaim = self.wallet.getMatchingConnectionsWithAvailableClaim(
claimName)
matchingLinkAndReceivedClaim = []
for li, cl in matchingLinkAndAvailableClaim:
name, version, origin = cl
schemaKeyId = ID(
SchemaKey(name=name, version=version, issuerId=origin))
schema = await self.prover.wallet.getSchema(schemaKeyId)
claimAttrs = OrderedDict()
for attr in schema.attrNames:
claimAttrs[attr] = None
attrs = None
try:
attrs = await self.prover.wallet.getClaimAttributes(schemaKeyId)
except ValueError:
pass # it means no claim was issued
if attrs:
if set(claimAttrs.keys()).intersection(attrs.keys()):
for k in claimAttrs.keys():
claimAttrs[k] = attrs[k].raw
matchingLinkAndReceivedClaim.append((li, cl, claimAttrs))
return matchingLinkAndReceivedClaim
async def getMatchingRcvdClaimsAsync(self, attributes):
linksAndReceivedClaim = await self.getMatchingConnectionsWithReceivedClaimAsync()
attributes = set(attributes)
matchingLinkAndRcvdClaim = []
for li, cl, issuedAttrs in linksAndReceivedClaim:
if attributes.intersection(issuedAttrs.keys()):
matchingLinkAndRcvdClaim.append((li, cl, issuedAttrs))
return matchingLinkAndRcvdClaim
async def getClaimsUsedForAttrs(self, attributes):
allMatchingClaims = await self.getMatchingConnectionsWithReceivedClaimAsync()
alreadySatisfiedKeys = {}
claimsToUse = []
alreadyAddedClaims = []
for li, cl, issuedAttrs in allMatchingClaims:
issuedClaimKeys = issuedAttrs.keys()
for key in attributes.keys():
if key not in alreadySatisfiedKeys and key in issuedClaimKeys:
if li not in alreadyAddedClaims:
claimsToUse.append((li, cl, issuedAttrs))
alreadySatisfiedKeys[key] = True
alreadyAddedClaims.append(li)
return claimsToUse