Skip to content

Commit

Permalink
feat: ZKP verification (#792)
Browse files Browse the repository at this point in the history
Signed-off-by: Bassam Riman <[email protected]>
Signed-off-by: Benjamin Voiturier <[email protected]>
Co-authored-by: bvoiturier <[email protected]>
Signed-off-by: Shota Jolbordi <[email protected]>
  • Loading branch information
2 people authored and Shota Jolbordi committed Mar 6, 2024
1 parent 01d9d4a commit 7c3bf67
Show file tree
Hide file tree
Showing 55 changed files with 3,433 additions and 629 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ lazy val D = new {
"com.github.dasniko" % "testcontainers-keycloak" % V.testContainersJavaKeycloak % Test

val doobiePostgres: ModuleID = "org.tpolecat" %% "doobie-postgres" % V.doobie
val doobiePostgresCirce: ModuleID = "org.tpolecat" %% "doobie-postgres-circe" % V.doobie
val doobieHikari: ModuleID = "org.tpolecat" %% "doobie-hikari" % V.doobie
val flyway: ModuleID = "org.flywaydb" % "flyway-core" % V.flyway

Expand All @@ -160,7 +161,7 @@ lazy val D = new {

// LIST of Dependencies
val doobieDependencies: Seq[ModuleID] =
Seq(doobiePostgres, doobieHikari, flyway)
Seq(doobiePostgres, doobiePostgresCirce, doobieHikari, flyway)
}

lazy val D_Shared = new {
Expand Down
110 changes: 108 additions & 2 deletions docs/docusaurus/credentials/present-proof.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Present proof

The [Present Proof Protocol](/docs/concepts/glossary#present-proof-protocol) allows:
Expand Down Expand Up @@ -57,6 +60,9 @@ To do this, he makes a `POST` request to the [`/present-proof/presentations`](/a
1. `connectionId`: This field represents the unique identifier of an existing connection between the verifier and the Holder/prover. It is for exchanging messages related to the protocol flow execution.
2. `challenge` and `domain`: The Verifier provides the random seed challenge and operational domain, and the Holder/Prover must sign the generated proof to protect from replay attacks.

<Tabs groupId="vc-formats">
<TabItem value="jwt" label="JWT">

```bash
curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
-H 'accept: application/json' \
Expand All @@ -72,6 +78,56 @@ curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
}'
```

</TabItem>
<TabItem value="anoncreds" label="AnonCreds">

```bash
curl -X 'POST' 'http://localhost:8070/prism-agent/present-proof/presentations' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H "apikey: $API_KEY" \
-d '{
"connectionId": "872ddfa9-4115-46c2-8a1b-22c24c7431d7",
"anoncredPresentationRequest": {
"requested_attributes": {
"attribute1": {
"name": "Attribute 1",
"restrictions": [
{
"cred_def_id": "credential_definition_id_of_attribute1"
}
],
"non_revoked": {
"from": 1635734400,
"to": 1735734400
}
}
},
"requested_predicates": {
"predicate1": {
"name": "age",
"p_type": ">=",
"p_value": 18,
"restrictions": [
{
"schema_id": "schema_id_of_predicate1"
}
],
"non_revoked": {
"from": 1635734400
}
}
},
"name": "Example Presentation Request",
"nonce": "1234567890",
"version": "1.0"
},
"credentialFormat": "AnonCreds"
}'
```
</TabItem>
</Tabs>

Upon execution, a new presentation request record gets created with an initial state of `RequestPending`. The Verifier PRISM Agent will send the presentation request message to the PRISM Agent of the Holder/Prover through the specified DIDComm connection. The record state then is updated to `RequestSent`.

The Verifier can retrieve the list of presentation records by making a `GET` request to the [`/present-proof/presentations`](/agent-api/#tag/Present-Proof/operation/getAllPresentation) endpoint:
Expand Down Expand Up @@ -121,6 +177,9 @@ curl -X 'GET' 'http://localhost:8090/prism-agent/present-proof/presentations' \

The Holder/Prover can then accept a specific request, generate the proof, and send it to the Verifier PRISM Agent by making a `PATCH` request to the [`/present-proof/presentations/{id}`](/agent-api/#tag/Present-Proof/operation/updatePresentation) endpoint:

<Tabs groupId="vc-formats">
<TabItem value="jwt" label="JWT">

```bash
curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{PRESENTATION_ID}' \
-H 'Content-Type: application/json' \
Expand All @@ -133,8 +192,39 @@ curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{

The Holder/Prover will have to provide the following information:
1. `presentationId`: The unique identifier of the presentation record to accept.
2. `proofId`: The unique identifier of the verifiable credential record to use as proof.
2. `proofId`: The unique identifier of the verifiable credential record to use as proof.

</TabItem>
<TabItem value="anoncreds" label="AnonCreds">

```bash
curl -X 'PATCH' 'http://localhost:8090/prism-agent/present-proof/presentations/{PRESENTATION_ID}' \
-H 'Content-Type: application/json' \
-H "apikey: $API_KEY" \
-d '{
"action": "request-accept",
"anoncredPresentationRequest":{
"credentialProofs":[
{
"credential":"3e849b98-f0fd-4cb4-ae96-9ea527a76267",
"requestedAttribute":[
"age"
],
"requestedPredicate":[
"age"
]
}
]
}
}'
```
</TabItem>
</Tabs>

The Holder/Prover will have to provide the following information:
1. `presentationId`: The unique identifier of the presentation record to accept.
2. `anoncredPresentationRequest`: A list of credential unique identifier with the attribute and predicate the credential is answering for.

The record state is updated to `PresentationPending` and processed by the Holder/Prover PRISM Agent. The agent will automatically generate the proof presentation, change the state to `PresentationGenerated`, and will eventually send it to the Verifier Agent, and change the state to `PresentationSent`.

```mermaid
Expand All @@ -152,4 +242,20 @@ stateDiagram-v2

The following diagram shows the end-to-end flow for a verifier to request and verify a proof presentation from a Holder/prover.

![](present-proof-flow.png)
### JWT Present Proof Flow Diagram
![](present-proof-flow.png)
### Anoncreds Present Proof Flow Diagram
![](anoncreds-present-proof-flow.png)

<Tabs groupId="vc-formats">
<TabItem value="jwt" label="JWT">

![](present-proof-flow.png)

</TabItem>
<TabItem value="anoncreds" label="AnonCreds">

![](anoncreds-present-proof-flow.png)

</TabItem>
</Tabs>
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ object AnoncredLib {
version: String, // SCHEMA_Version
attr_names: AttributeNames,
issuer_id: IssuerId, // ISSUER_DID
): SchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id)
): AnoncredSchemaDef = uniffi.anoncreds_wrapper.Schema.apply(name, version, attr_names.toSeq.asJava, issuer_id)

// issuer
def createCredDefinition(
issuer_id: String,
schema: SchemaDef,
schema: AnoncredSchemaDef,
tag: String,
supportRevocation: Boolean,
signature_type: uniffi.anoncreds_wrapper.SignatureType.CL.type = uniffi.anoncreds_wrapper.SignatureType.CL
) = {
): AnoncredCreateCredentialDefinition = {
val credentialDefinition: uniffi.anoncreds_wrapper.IssuerCreateCredentialDefinitionReturn =
uniffi.anoncreds_wrapper
.Issuer()
Expand All @@ -40,7 +40,7 @@ object AnoncredLib {
uniffi.anoncreds_wrapper.CredentialDefinitionConfig(supportRevocation)
)

CreateCredentialDefinition(
AnoncredCreateCredentialDefinition(
credentialDefinition.getCredentialDefinition(),
credentialDefinition.getCredentialDefinitionPrivate(),
credentialDefinition.getCredentialKeyCorrectnessProof()
Expand All @@ -49,9 +49,9 @@ object AnoncredLib {

// issuer
def createOffer(
credentialDefinition: CreateCredentialDefinition,
credentialDefinition: AnoncredCreateCredentialDefinition,
credentialDefinitionId: String
): CredentialOffer =
): AnoncredCredentialOffer =
uniffi.anoncreds_wrapper
.Issuer()
.createCredentialOffer(
Expand All @@ -62,15 +62,15 @@ object AnoncredLib {

// holder
def createCredentialRequest(
linkSecret: LinkSecretWithId,
credentialDefinition: CredentialDefinition,
credentialOffer: CredentialOffer,
linkSecret: AnoncredLinkSecretWithId,
credentialDefinition: AnoncredCredentialDefinition,
credentialOffer: AnoncredCredentialOffer,
entropy: String = {
val tmp = scala.util.Random()
tmp.setSeed(java.security.SecureRandom.getInstanceStrong().nextLong())
tmp.nextString(80)
}
): CreateCrendentialRequest = {
): AnoncredCreateCrendentialRequest = {
val credentialRequest =
uniffi.anoncreds_wrapper
.Prover()
Expand All @@ -83,16 +83,16 @@ object AnoncredLib {
credentialOffer, // CredentialOffer credential_offer
)

CreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata())
AnoncredCreateCrendentialRequest(credentialRequest.getRequest(), credentialRequest.getMetadata())
}

// holder
def processCredential(
credential: Credential,
metadata: CredentialRequestMetadata,
linkSecret: LinkSecretWithId,
credentialDefinition: CredentialDefinition,
): Credential = {
credential: AnoncredCredential,
metadata: AnoncredCredentialRequestMetadata,
linkSecret: AnoncredLinkSecretWithId,
credentialDefinition: AnoncredCredentialDefinition,
): AnoncredCredential = {
uniffi.anoncreds_wrapper
.Prover()
.processCredential(
Expand All @@ -106,16 +106,16 @@ object AnoncredLib {

// issuer
def createCredential(
credentialDefinition: CredentialDefinition,
credentialDefinitionPrivate: CredentialDefinitionPrivate,
credentialOffer: CredentialOffer,
credentialRequest: CredentialRequest,
credentialDefinition: AnoncredCredentialDefinition,
credentialDefinitionPrivate: AnoncredCredentialDefinitionPrivate,
credentialOffer: AnoncredCredentialOffer,
credentialRequest: AnoncredCredentialRequest,
attributeValues: Seq[(String, String)]
// java.util.List[AttributeValues] : java.util.List[AttributeValues]
// revocationRegistryId : String
// revocationStatusList : RevocationStatusList
// credentialRevocationConfig : CredentialRevocationConfig
): Credential = {
): AnoncredCredential = {
uniffi.anoncreds_wrapper
.Issuer()
.createCredential(
Expand All @@ -140,13 +140,13 @@ object AnoncredLib {
// [info] Caused by: Predicate is not satisfied

def createPresentation(
presentationRequest: PresentationRequest,
credentialRequests: Seq[CredentialAndRequestedAttributesPredicates],
presentationRequest: AnoncredPresentationRequest,
credentialRequests: Seq[AnoncredCredentialRequests],
selfAttested: Map[String, String],
linkSecret: LinkSecret,
schemas: Map[SchemaId, SchemaDef],
credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition],
): Either[uniffi.anoncreds_wrapper.AnoncredsException.CreatePresentationException, Presentation] = {
linkSecret: AnoncredLinkSecret,
schemas: Map[SchemaId, AnoncredSchemaDef],
credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition],
): Either[uniffi.anoncreds_wrapper.AnoncredsException.CreatePresentationException, AnoncredPresentation] = {
try {
Right(
uniffi.anoncreds_wrapper
Expand Down Expand Up @@ -181,10 +181,10 @@ object AnoncredLib {

// FIXME its always return false ....
def verifyPresentation(
presentation: Presentation,
presentationRequest: PresentationRequest,
schemas: Map[SchemaId, SchemaDef],
credentialDefinitions: Map[CredentialDefinitionId, CredentialDefinition],
presentation: AnoncredPresentation,
presentationRequest: AnoncredPresentationRequest,
schemas: Map[SchemaId, AnoncredSchemaDef],
credentialDefinitions: Map[CredentialDefinitionId, AnoncredCredentialDefinition],
): Boolean = {
uniffi.anoncreds_wrapper
.Verifier()
Expand Down
Loading

0 comments on commit 7c3bf67

Please sign in to comment.