Skip to content

Commit

Permalink
Interoperability tests (hyperledger-indy#67)
Browse files Browse the repository at this point in the history
* added toInt and crypto_int_to_str methods

* added to_str_dict

* added str()

* added from_str_dict

* added test for serialization/deserialization

* added NamedTupleStrSerializer

* added serialization for claim_request

* added test for claim_request

* Changed structure of claim

* fix types

* Added methods to/from dict

* Fixed error

* Fix error

* fix

* updated create proof according to new claim structure

* fix module

* Fix

* Updated ProofInput structure

* Changed  create proof and verify methods. Corrected and added tests

* Added schema_seq_no cheking

* Updated ClaimInfo

* Try resolve problem with AggregatedProof serialization

* Fix AggregatedProof serialization

* Updated find_claims function

* Corrected serialization

* Replaced claim_def_seq_no on issuer_did

* Renamed claim to claimAttributes

* Fixed mistakes

* Renamed AttributeValues to ClaimAttributeValues

* added test

* added claims

* test_works

* refactoring

* added test for verifier

* Replaced ProofInput on ProofRequest

* finished test for verifier

* changed deserialization

* added test for prover

* small changes

* fixed after merge

* small changes

* added config for socket

* Update Jenkinsfile
  • Loading branch information
MRJCrunch authored and ashcherbakov committed Jul 12, 2017
1 parent 6dc0144 commit bed9522
Show file tree
Hide file tree
Showing 17 changed files with 679 additions and 286 deletions.
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ def testWindowsNoDocker = {
}
}

testAndPublish(name, [ubuntu: testUbuntu, windows: testWindowsNoDocker, windowsNoDocker: testWindowsNoDocker])
testAndPublish(name, [ubuntu: [anoncreds: testUbuntu], windows: [anoncreds: testWindowsNoDocker], windowsNoDocker: [anoncreds: testWindowsNoDocker]])
2 changes: 1 addition & 1 deletion anoncreds/protocol/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
ZERO_INDEX = "0"

ATTRS = "attrs"
DELTA = "delta"
DELTA = "DELTA"
TVAL = "Tval"
KEYS = "keys"
NONCE = "nonce"
Expand Down
14 changes: 7 additions & 7 deletions anoncreds/protocol/prover.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
NonRevocationClaimInitializer, \
NonRevocationProofBuilder
from anoncreds.protocol.types import PrimaryClaim, NonRevocationClaim, Proof, \
InitProof, ProofInput, ProofClaims, \
InitProof, ProofRequest, ProofClaims, \
FullProof, \
Schema, ID, SchemaKey, ClaimRequest, Claims, RequestedProof, AggregatedProof, ProofInfo, ClaimAttributeValues
from anoncreds.protocol.utils import get_hash_as_int, isCryptoInteger
Expand Down Expand Up @@ -99,16 +99,16 @@ async def processClaims(self, allClaims: Dict[ID, Claims]):
res.append(await self.processClaim(schemaId, claim_attributes, claim_signature))
return res

async def presentProof(self, proofInput: ProofInput) -> FullProof:
async def presentProof(self, proofRequest: ProofRequest) -> FullProof:
"""
Presents a proof to the verifier.
:param proofInput: description of a proof to be presented (revealed
:param proofRequest: description of a proof to be presented (revealed
attributes, predicates, timestamps for non-revocation)
:return: a proof (both primary and non-revocation) and revealed attributes (initial non-encoded values)
"""
claims, requestedProof = await self._findClaims(proofInput)
proof = await self._prepareProof(claims, proofInput.nonce, requestedProof)
claims, requestedProof = await self._findClaims(proofRequest)
proof = await self._prepareProof(claims, proofRequest.nonce, requestedProof)
return proof

#
Expand Down Expand Up @@ -151,9 +151,9 @@ async def _initNonRevocationClaim(self, schemaId: ID,
# PRESENT PROOF
#

async def _findClaims(self, proofInput: ProofInput) -> (
async def _findClaims(self, proofRequest: ProofRequest) -> (
Dict[SchemaKey, ProofClaims], Dict[str, Any]):
revealedAttrs, predicates = proofInput.revealedAttrs, proofInput.predicates
revealedAttrs, predicates = proofRequest.verifiableAttributes, proofRequest.predicates

foundRevealedAttrs = {}
foundPredicates = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from anoncreds.protocol.revocation.accumulators.non_revocation_common import \
createTauListExpectedValues, \
createTauListValues
from anoncreds.protocol.types import T, NonRevocProof, ID, ProofInput
from anoncreds.protocol.types import T, NonRevocProof, ID, ProofRequest
from anoncreds.protocol.utils import int_to_ZR
from anoncreds.protocol.wallet.wallet import Wallet
from config.config import cmod
Expand All @@ -14,16 +14,16 @@ class NonRevocationProofVerifier:
def __init__(self, wallet: Wallet):
self._wallet = wallet

async def verifyNonRevocation(self, proofInput: ProofInput, schema_seq_no,
async def verifyNonRevocation(self, proofRequest: ProofRequest, schema_seq_no,
cHash, nonRevocProof: NonRevocProof) \
-> Sequence[T]:
if await self._wallet.shouldUpdateAccumulator(
schemaId=ID(seqId=schema_seq_no),
ts=proofInput.ts,
seqNo=proofInput.seqNo):
ts=proofRequest.ts,
seqNo=proofRequest.seqNo):
await self._wallet.updateAccumulator(schemaId=ID(schemaId=schema_seq_no),
ts=proofInput.ts,
seqNo=proofInput.seqNo)
ts=proofRequest.ts,
seqNo=proofRequest.seqNo)

pkR = await self._wallet.getPublicKeyRevocation(ID(schemaId=schema_seq_no))
accum = await self._wallet.getAccumulator(ID(schemaId=schema_seq_no))
Expand Down
138 changes: 98 additions & 40 deletions anoncreds/protocol/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
fromDictWithStrValues, deserializeFromStr, encodeAttr, crypto_int_to_str, to_crypto_int, isCryptoInteger, \
intToArrayBytes, bytesToInt
from config.config import cmod

from typing import NamedTuple
import uuid

class AttribType:
def __init__(self, name: str, encode: bool):
Expand Down Expand Up @@ -279,15 +280,15 @@ def from_str_dict(cls, d):
attrName = d['attr_name']
value = d['value']
type = d['p_type']
schema_seq_no = int(d['schema_seq_no']) if d['schema_seq_no'] else None
issuer_did = int(d['issuer_did']) if d['issuer_did'] else None
return Predicate(attrName=attrName, value=value, type=type,
schema_seq_no = int(d['schema_seq_no']) if (('schema_seq_no' in d) and d['schema_seq_no']) else None
issuer_did = int(d['issuer_did']) if (('issuer_did' in d) and d['issuer_did']) else None
return PredicateGE(attrName=attrName, value=value, type=type,
schema_seq_no=schema_seq_no, issuer_did=issuer_did)


# TODO: now we consdider only >= predicate. Support other types of predicates
class PredicateGE(Predicate):
def __new__(cls, attrName, value, type='ge', schema_seq_no=None, issuer_did=None):
def __new__(cls, attrName, value, type='GE', schema_seq_no=None, issuer_did=None):
return super(PredicateGE, cls).__new__(cls, attrName, value, type, schema_seq_no, issuer_did)


Expand Down Expand Up @@ -428,39 +429,8 @@ def __str__(self):
return os.linesep.join(rtn)


class ProofInput(
namedtuple('ProofInput', 'nonce, revealedAttrs, predicates, ts, seqNo'),
NamedTupleStrSerializer):
def __new__(cls, nonce=None, revealedAttrs=None, predicates=None, ts=None, seqNo=None):
return super(ProofInput, cls).__new__(cls, nonce, revealedAttrs or {},
predicates or {},
ts, seqNo)

@classmethod
def fromStrDict(cls, d):
d = fromDictWithStrValues(d)
revealedAttrs = {k: AttributeInfo.fromStrDict(v) for k, v in d['revealedAttrs'].items()}
predicates = {k: Predicate.fromStrDict(v) for k, v in d['predicates'].items()}
result = cls(**d)
return result._replace(revealedAttrs=revealedAttrs, predicates=predicates)

def to_str_dict(self):
return {
'nonce': str(self.nonce),
'revealedAttrs': {k: v.to_str_dict() for k, v in self.revealedAttrs.items()},
'predicates': {k: v.to_str_dict() for k, v in self.predicates.items()}
}

@classmethod
def from_str_dict(cls, d):
nonce = int(d['nonce'])
revealedAttrs = {k: AttributeInfo.from_str_dict(v) for k, v in d['revealedAttrs'].items()}
predicates = {k: Predicate.from_str_dict(v) for k, v in d['predicates'].items()}
return ProofInput(nonce=nonce, revealedAttrs=revealedAttrs, predicates=predicates)


class AttributeInfo(
namedtuple('ProofInput', 'name, schema_seq_no, issuer_did'),
namedtuple('AttributeInfo', 'name, schema_seq_no, issuer_did'),
NamedTupleStrSerializer):
def __new__(cls, name=None, schema_seq_no=None, issuer_did=None):
return super(AttributeInfo, cls).__new__(cls, name, schema_seq_no, issuer_did)
Expand All @@ -475,7 +445,7 @@ def to_str_dict(self):
@classmethod
def from_str_dict(cls, d):
schema_seq_no = int(d['schema_seq_no']) if d['schema_seq_no'] else None
issuer_did = int(d['issuer_did']) if d['issuer_did'] else None
issuer_did = int(d['issuer_did']) if (('issuer_did' in d) and d['issuer_did']) else None
name = d['name']
return AttributeInfo(name, schema_seq_no, issuer_did)

Expand Down Expand Up @@ -783,7 +753,7 @@ def to_str_dict(self):
def from_str_dict(cls, d, n):
aggregatedProof = AggregatedProof.from_str_dict(d['aggregated_proof'])
requestedProof = RequestedProof.from_str_dict(d['requested_proof'])
proofs = {k: Proof.from_str_dict(v['proof'], n) for k, v in d['proofs']}
proofs = {item[0]: ProofInfo.from_str_dict(item[1], n[i]) for i, item in enumerate(d['proofs'].items())}

return FullProof(aggregatedProof=aggregatedProof, requestedProof=requestedProof, proofs=proofs)

Expand Down Expand Up @@ -839,10 +809,98 @@ def __new__(cls, raw=None, encoded=None):
return super(ClaimAttributeValues, cls).__new__(cls, raw, encoded)

def to_str_dict(self):
return [self.raw, str(self.encoded)]
return [str(self.raw), str(self.encoded)]

@classmethod
def from_str_dict(cls, d):
raw = d[0]
encoded = int(to_crypto_int(d[1]))
return ClaimAttributeValues(raw=raw, encoded=encoded)


AvailableClaim = NamedTuple("AvailableClaim", [("name", str),
("version", str),
("origin", str)])


class ProofRequest:
def __init__(self, name, version, nonce, attributes=[], verifiableAttributes=[], predicates=[]):
self.name = name
self.version = version
self.nonce = nonce
self.attributes = attributes
self.verifiableAttributes = \
{str(uuid.uuid4()): AttributeInfo(name=a) for a in verifiableAttributes} if \
isinstance(verifiableAttributes, list) else verifiableAttributes
self.predicates = {str(uuid.uuid4()): PredicateGE(attrName=p['attrName'], value=p['value']) for p in
predicates} if isinstance(predicates, list) else predicates
self.fulfilledByClaims = []
self.selfAttestedAttrs = {}
self.ts = None
self.seqNo = None
# TODO _F_ need to add support for predicates on unrevealed attibutes

def __eq__(self, other):
return self.__dict__ == other.__dict__

@property
def toDict(self):
return {
"name": self.name,
"version": self.version,
"nonce": self.nonce,
"attributes": self.attributes,
"requested_attrs": self.verifiableAttributes
}

def to_str_dict(self):
return {
"name": self.name,
"version": self.version,
"nonce": str(self.nonce),
"requested_attrs": {k: v.to_str_dict() for k, v in self.verifiableAttributes.items()},
"requested_predicates": {k: v.to_str_dict() for k, v in self.predicates.items()}
}

@staticmethod
def from_str_dict(d):
return ProofRequest(name=d['name'],
version=d['version'],
nonce=int(d['nonce']),
attributes=d['attributes'] if 'attributes' in d else [],
verifiableAttributes={k: AttributeInfo.from_str_dict(v) for k, v in
d['requested_attrs'].items()},
predicates={k: PredicateGE.from_str_dict(v) for k, v in d['requested_predicates'].items()})

@property
def attributeValues(self):
return \
'Attributes:' + '\n ' + \
format("\n ".join(
['{}: {}'.format(k, v)
for k, v in self.attributes])) + '\n'

@property
def verifiableClaimAttributeValues(self):
return \
'Verifiable Attributes:' + '\n ' + \
format("\n ".join(
['{}'.format(v.name)
for k, v in self.verifiableAttributes.items()])) + '\n'

@property
def predicateValues(self):
return \
'Predicates:' + '\n ' + \
format("\n ".join(
['{}'.format(v.attrName)
for k, v in self.predicates.items()])) + '\n'

@property
def fixedInfo(self):
return 'Status: Requested' + '\n' \
'Name: ' + self.name + '\n' \
'Version: ' + self.version + '\n'

def __str__(self):
return self.fixedInfo + self.attributeValues + self.verifiableClaimAttributeValues
18 changes: 9 additions & 9 deletions anoncreds/protocol/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
PrimaryProofVerifier
from anoncreds.protocol.revocation.accumulators.non_revocation_proof_verifier import \
NonRevocationProofVerifier
from anoncreds.protocol.types import FullProof, ProofInput
from anoncreds.protocol.types import FullProof, ProofRequest
from anoncreds.protocol.utils import get_hash_as_int, isCryptoInteger
from anoncreds.protocol.wallet.wallet import Wallet
from config.config import cmod
Expand All @@ -24,37 +24,37 @@ def verifierId(self):
def generateNonce(self):
return cmod.integer(cmod.randomBits(LARGE_NONCE))

async def verify(self, proofInput: ProofInput, proof: FullProof):
async def verify(self, proofRequest: ProofRequest, proof: FullProof):
"""
Verifies a proof from the prover.
:param proofInput: description of a proof to be presented (revealed
:param proofRequest: description of a proof to be presented (revealed
attributes, predicates, timestamps for non-revocation)
:param proof: a proof
:return: True if verified successfully and false otherwise.
"""

if proofInput.revealedAttrs.keys() != proof.requestedProof.revealed_attrs.keys():
if proofRequest.verifiableAttributes.keys() != proof.requestedProof.revealed_attrs.keys():
raise ValueError('Received attributes ={} do not correspond to requested={}'.format(
proof.requestedProof.revealed_attrs.keys(), proofInput.revealedAttrs.keys()))
proof.requestedProof.revealed_attrs.keys(), proofRequest.verifiableAttributes.keys()))

if proofInput.predicates.keys() != proof.requestedProof.predicates.keys():
if proofRequest.predicates.keys() != proof.requestedProof.predicates.keys():
raise ValueError('Received predicates ={} do not correspond to requested={}'.format(
proof.requestedProof.predicates.keys(), proofInput.predicates.keys()))
proof.requestedProof.predicates.keys(), proofRequest.predicates.keys()))

TauList = []
for (uuid, proofItem) in proof.proofs.items():
if proofItem.proof.nonRevocProof:
TauList += await self._nonRevocVerifier.verifyNonRevocation(
proofInput, proofItem.schema_seq_no, proof.aggregatedProof.cHash,
proofRequest, proofItem.schema_seq_no, proof.aggregatedProof.cHash,
proofItem.proof.nonRevocProof)
if proofItem.proof.primaryProof:
TauList += await self._primaryVerifier.verify(proofItem.schema_seq_no,
proof.aggregatedProof.cHash,
proofItem.proof.primaryProof)

CHver = self._get_hash(proof.aggregatedProof.CList, self._prepare_collection(TauList),
cmod.integer(proofInput.nonce))
cmod.integer(proofRequest.nonce))

return CHver == proof.aggregatedProof.cHash

Expand Down
9 changes: 4 additions & 5 deletions anoncreds/test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from anoncreds.protocol.prover import Prover
from anoncreds.protocol.repo.attributes_repo import AttributeRepoInMemory
from anoncreds.protocol.repo.public_repo import PublicRepoInMemory
from anoncreds.protocol.types import AttribDef, AttribType, ID, ProofInput
from anoncreds.protocol.types import AttribDef, AttribType, ID, ProofRequest
from anoncreds.protocol.verifier import Verifier
from anoncreds.protocol.wallet.issuer_wallet import IssuerWalletInMemory
from anoncreds.protocol.wallet.prover_wallet import ProverWalletInMemory
Expand Down Expand Up @@ -273,7 +273,6 @@ def genNonce(verifier):
return verifier.generateNonce()


async def presentProofAndVerify(verifier: Verifier, proofInput: ProofInput, prover):
proofInput = ProofInput(verifier.generateNonce(), proofInput.revealedAttrs, proofInput.predicates)
proof = await prover.presentProof(proofInput)
return await verifier.verify(proofInput, proof)
async def presentProofAndVerify(verifier: Verifier, proofRequest: ProofRequest, prover):
proof = await prover.presentProof(proofRequest)
return await verifier.verify(proofRequest, proof)
Loading

0 comments on commit bed9522

Please sign in to comment.